diff options
-rw-r--r-- | Documentation/fetch-options.txt | 3 | ||||
-rw-r--r-- | Documentation/git-checkout.txt | 76 | ||||
-rw-r--r-- | Documentation/git-clone.txt | 24 | ||||
-rw-r--r-- | Documentation/git-cvsimport.txt | 20 | ||||
-rw-r--r-- | Documentation/git-show-branch.txt | 50 | ||||
-rw-r--r-- | Documentation/howto/revert-branch-rebase.txt | 24 | ||||
-rw-r--r-- | Documentation/tutorial.txt | 49 | ||||
-rw-r--r-- | Makefile | 30 | ||||
-rw-r--r-- | daemon.c | 3 | ||||
-rw-r--r-- | describe.c | 40 | ||||
-rw-r--r-- | exec_cmd.c | 117 | ||||
-rw-r--r-- | exec_cmd.h | 10 | ||||
-rw-r--r-- | fetch-clone.c | 7 | ||||
-rwxr-xr-x | git-checkout.sh | 51 | ||||
-rwxr-xr-x | git-clone.sh | 66 | ||||
-rwxr-xr-x | git-cvsimport.perl | 67 | ||||
-rwxr-xr-x | git-fetch.sh | 5 | ||||
-rwxr-xr-x | git-format-patch.sh | 141 | ||||
-rwxr-xr-x | git-merge-octopus.sh | 22 | ||||
-rwxr-xr-x | git-push.sh | 21 | ||||
-rw-r--r-- | git.c | 50 | ||||
-rw-r--r-- | receive-pack.c | 4 | ||||
-rw-r--r-- | run-command.c | 9 | ||||
-rw-r--r-- | run-command.h | 2 | ||||
-rw-r--r-- | send-pack.c | 9 | ||||
-rw-r--r-- | shell.c | 7 | ||||
-rw-r--r-- | show-branch.c | 100 | ||||
-rwxr-xr-x | t/t1200-tutorial.sh | 6 | ||||
-rw-r--r-- | upload-pack.c | 7 |
29 files changed, 751 insertions, 269 deletions
diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index 1fe8423b94..e624d3d0ee 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -24,6 +24,9 @@ flag lets all tags and their associated objects be downloaded. +-k, \--keep:: + Keep downloaded pack. + -u, \--update-head-ok:: By default `git-fetch` refuses to update the head which corresponds to the current branch. This flag disables the diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 9442c66b18..df9a618674 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -7,7 +7,7 @@ git-checkout - Checkout and switch to a branch. SYNOPSIS -------- -'git-checkout' [-f] [-b <new_branch>] [<branch>] [<paths>...] +'git-checkout' [-f] [-b <new_branch>] [-m] [<branch>] [<paths>...] DESCRIPTION ----------- @@ -34,6 +34,19 @@ OPTIONS -b:: Create a new branch and start it at <branch>. +-m:: + If you have local modifications to a file that is + different between the current branch and the branch you + are switching to, the command refuses to switch + branches, to preserve your modifications in context. + With this option, a three-way merge between the current + branch, your working tree contents, and the new branch + is done, and you will be on the new branch. ++ +When a merge conflict happens, the index entries for conflicting +paths are left unmerged, and you need to resolve the conflicts +and mark the resolved paths with `git update-index`. + <new_branch>:: Name for the new branch. @@ -42,13 +55,13 @@ OPTIONS commit. Defaults to HEAD. -EXAMPLE -------- +EXAMPLES +-------- -The following sequence checks out the `master` branch, reverts +. The following sequence checks out the `master` branch, reverts the `Makefile` to two revisions back, deletes hello.c by mistake, and gets it back from the index. - ++ ------------ $ git checkout master <1> $ git checkout master~2 Makefile <2> @@ -59,15 +72,64 @@ $ git checkout hello.c <3> <2> take out a file out of other commit <3> or "git checkout -- hello.c", as in the next example. ------------ - ++ If you have an unfortunate branch that is named `hello.c`, the last step above would be confused as an instruction to switch to that branch. You should instead write: - ++ ------------ $ git checkout -- hello.c ------------ +. After working in a wrong branch, switching to the correct +branch you would want to is done with: ++ +------------ +$ git checkout mytopic +------------ ++ +However, your "wrong" branch and correct "mytopic" branch may +differ in files that you have locally modified, in which case, +the above checkout would fail like this: ++ +------------ +$ git checkout mytopic +fatal: Entry 'frotz' not uptodate. Cannot merge. +------------ ++ +You can give the `-m` flag to the command, which would try a +three-way merge: ++ +------------ +$ git checkout -m mytopic +Auto-merging frotz +------------ ++ +After this three-way merge, the local modifications are _not_ +registered in your index file, so `git diff` would show you what +changes you made since the tip of the new branch. + +. When a merge conflict happens during switching branches with +the `-m` option, you would see something like this: ++ +------------ +$ git checkout -m mytopic +Auto-merging frotz +merge: warning: conflicts during merge +ERROR: Merge conflict in frotz +fatal: merge program failed +------------ ++ +At this point, `git diff` shows the changes cleanly merged as in +the previous example, as well as the changes in the conflicted +files. Edit and resolve the conflict and mark it resolved with +`git update-index` as usual: ++ +------------ +$ edit frotz +$ git update-index frotz +------------ + Author ------ diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index 790b87b231..8488202e3c 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -9,7 +9,7 @@ git-clone - Clones a repository. SYNOPSIS -------- [verse] -'git-clone' [-l [-s]] [-q] [-n] [-o <name>] [-u <upload-pack>] +'git-clone' [-l [-s]] [-q] [-n] [--naked] [-o <name>] [-u <upload-pack>] <repository> [<directory>] DESCRIPTION @@ -58,6 +58,12 @@ OPTIONS -n:: No checkout of HEAD is performed after the clone is complete. +--naked:: + Make a 'naked' GIT repository. That is, instead of + creating `<directory>` and placing the administrative + files in `<directory>/.git`, make the `<directory>` + itself the `$GIT_DIR`. This implies `-n` option. + -o <name>:: Instead of using the branch name 'origin' to keep track of the upstream repository, use <name> instead. Note @@ -103,6 +109,22 @@ $ cd copy $ git show-branch ------------ + +Create a naked repository to publish your changes to the public:: ++ +------------ +$ git clone --naked -l /home/proj/.git /pub/scm/proj.git +------------ + + +Create a repository on the kernel.org machine that borrows from Linus:: ++ +------------ +$ git clone --naked -l -s /pub/scm/.../torvalds/linux-2.6.git \ + /pub/scm/.../me/subsys-2.6.git +------------ + + Author ------ Written by Linus Torvalds <torvalds@osdl.org> diff --git a/Documentation/git-cvsimport.txt b/Documentation/git-cvsimport.txt index 01ca7ef96f..dfe86ceea3 100644 --- a/Documentation/git-cvsimport.txt +++ b/Documentation/git-cvsimport.txt @@ -89,6 +89,26 @@ If you need to pass multiple options, separate them with a comma. -s <subst>:: Substitute the character "/" in branch names with <subst> +-A <author-conv-file>:: + CVS by default uses the unix username when writing its + commit logs. Using this option and an author-conv-file + in this format + + exon=Andreas Ericsson <ae@op5.se> + spawn=Simon Pawn <spawn@frog-pond.org> + + git-cvsimport will make it appear as those authors had + their GIT_AUTHOR_NAME and GIT_AUTHOR_EMAIL set properly + all along. + + For convenience, this data is saved to $GIT_DIR/cvs-authors + each time the -A option is provided and read from that same + file each time git-cvsimport is run. + + It is not recommended to use this feature if you intend to + export changes back to CVS again later with + git-link[1]::git-cvsexportcommit. + OUTPUT ------ If '-v' is specified, the script reports what it is doing. diff --git a/Documentation/git-show-branch.txt b/Documentation/git-show-branch.txt index 5b76f3b99e..7b1a9c9875 100644 --- a/Documentation/git-show-branch.txt +++ b/Documentation/git-show-branch.txt @@ -7,7 +7,10 @@ git-show-branch - Show branches and their commits. SYNOPSIS -------- -'git-show-branch [--all] [--heads] [--tags] [--topo-order] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [<rev> | <glob>]...' +[verse] +git-show-branch [--all] [--heads] [--tags] [--topo-order] [--current] + [--more=<n> | --list | --independent | --merge-base] + [--no-name | --sha1-name] [<rev> | <glob>]... DESCRIPTION ----------- @@ -18,6 +21,9 @@ and/or $GIT_DIR/refs/tags) semi-visually. It cannot show more than 29 branches and commits at a time. +It uses `showbranch.default` multi-valued configuration items if +no <rev> nor <glob> is given on the command line. + OPTIONS ------- @@ -35,6 +41,11 @@ OPTIONS Show all refs under $GIT_DIR/refs, $GIT_DIR/refs/heads, and $GIT_DIR/refs/tags, respectively. +--current:: + With this option, the command includes the current + branch to the list of revs to be shown when it is not + given on the command line. + --topo-order:: By default, the branches and their commits are shown in reverse chronological order. This option makes them @@ -50,7 +61,7 @@ OPTIONS tree. --list:: - Synomym to `--more=-1` + Synonym to `--more=-1` --merge-base:: Instead of showing the commit list, just act like the @@ -78,13 +89,14 @@ OUTPUT ------ Given N <references>, the first N lines are the one-line description from their commit message. The branch head that is -pointed at by $GIT_DIR/HEAD is prefixed with an asterisk '*' -character while other heads are prefixed with a '!' character. +pointed at by $GIT_DIR/HEAD is prefixed with an asterisk `*` +character while other heads are prefixed with a `!` character. Following these N lines, one-line log for each commit is displayed, indented N places. If a commit is on the I-th -branch, the I-th indentation character shows a '+' sign; -otherwise it shows a space. Each commit shows a short name that +branch, the I-th indentation character shows a `+` sign; +otherwise it shows a space. Merge commits are denoted by +a `-` sign. Each commit shows a short name that can be used as an extended SHA1 to name that commit. The following example shows three branches, "master", "fixes" @@ -92,7 +104,7 @@ and "mhf": ------------------------------------------------ $ git show-branch master fixes mhf -! [master] Add 'git show-branch'. +* [master] Add 'git show-branch'. ! [fixes] Introduce "reset type" flag to "git reset" ! [mhf] Allow "+remote:local" refspec to cause --force when fetching. --- @@ -106,13 +118,33 @@ $ git show-branch master fixes mhf + [mhf~6] Retire git-parse-remote. + [mhf~7] Multi-head fetch. + [mhf~8] Start adding the $GIT_DIR/remotes/ support. -+++ [master] Add 'git show-branch'. +*++ [master] Add 'git show-branch'. ------------------------------------------------ These three branches all forked from a common commit, [master], whose commit message is "Add 'git show-branch'. "fixes" branch adds one commit 'Introduce "reset type"'. "mhf" branch has many -other commits. +other commits. The current branch is "master". + + +EXAMPLE +------- + +If you keep your primary branches immediately under +`$GIT_DIR/refs/heads`, and topic branches in subdirectories of +it, having the following in the configuration file may help: + +------------ +[showbranch] + default = --topo-order + default = heads/* + +------------ + +With this,`git show-branch` without extra parameters would show +only the primary branches. In addition, if you happen to be on +your topic branch, it is shown as well. + Author diff --git a/Documentation/howto/revert-branch-rebase.txt b/Documentation/howto/revert-branch-rebase.txt index 5a7e0cfe05..d10476b56e 100644 --- a/Documentation/howto/revert-branch-rebase.txt +++ b/Documentation/howto/revert-branch-rebase.txt @@ -32,16 +32,16 @@ merge introduced 5 commits or so: ------------------------------------------------ $ git show-branch --more=4 master master^2 | head -! [master] Merge refs/heads/portable from http://www.cs.berkeley.... +* [master] Merge refs/heads/portable from http://www.cs.berkeley.... ! [master^2] Replace C99 array initializers with code. -- -+ [master] Merge refs/heads/portable from http://www.cs.berkeley.... -++ [master^2] Replace C99 array initializers with code. -++ [master^2~1] Replace unsetenv() and setenv() with older putenv(). -++ [master^2~2] Include sys/time.h in daemon.c. -++ [master^2~3] Fix ?: statements. -++ [master^2~4] Replace zero-length array decls with []. -+ [master~1] tutorial note about git branch +- [master] Merge refs/heads/portable from http://www.cs.berkeley.... +*+ [master^2] Replace C99 array initializers with code. +*+ [master^2~1] Replace unsetenv() and setenv() with older putenv(). +*+ [master^2~2] Include sys/time.h in daemon.c. +*+ [master^2~3] Fix ?: statements. +*+ [master^2~4] Replace zero-length array decls with []. +* [master~1] tutorial note about git branch ------------------------------------------------ The '--more=4' above means "after we reach the merge base of refs, @@ -193,8 +193,8 @@ $ git show-branch --more=1 master pu rc + [pu~4] Document "git cherry-pick" and "git revert" + [pu~5] Remove git-apply-patch-script. + [pu~6] Redo "revert" using three-way merge machinery. - + [rc] Merge refs/heads/master from . -+++ [master] Revert "Replace zero-length array decls with []." - + [rc~1] Merge refs/heads/master from . -+++ [master~1] Merge refs/heads/portable from http://www.cs.berkeley.... + - [rc] Merge refs/heads/master from . +++* [master] Revert "Replace zero-length array decls with []." + - [rc~1] Merge refs/heads/master from . +... [master~1] Merge refs/heads/portable from http://www.cs.berkeley.... ------------------------------------------------ diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt index edd91cbbe4..b8fa299920 100644 --- a/Documentation/tutorial.txt +++ b/Documentation/tutorial.txt @@ -968,8 +968,8 @@ $ git show-branch master mybranch * [master] Merge work in mybranch ! [mybranch] Some work. -- -+ [master] Merge work in mybranch -++ [mybranch] Some work. +- [master] Merge work in mybranch +*+ [mybranch] Some work. ------------------------------------------------ The first two lines indicate that it is showing the two branches @@ -979,7 +979,8 @@ top-of-the-tree commits, you are currently on `master` branch the later output lines is used to show commits contained in the `master` branch, and the second column for the `mybranch` branch. Three commits are shown along with their log messages. -All of them have plus `+` characters in the first column, which +All of them have non blank characters in the first column (`*` +shows an ordinary commit on the current branch, `.` is a merge commit), which means they are now part of the `master` branch. Only the "Some work" commit has the plus `+` character in the second column, because `mybranch` has not been merged to incorporate these @@ -1024,7 +1025,7 @@ $ git show-branch master mybranch ! [master] Merge work in mybranch * [mybranch] Merge work in mybranch -- -++ [master] Merge work in mybranch +-- [master] Merge work in mybranch ------------------------------------------------ @@ -1199,9 +1200,9 @@ $ git show-branch --more=3 master mybranch ! [master] Merge work in mybranch * [mybranch] Merge work in mybranch -- -++ [master] Merge work in mybranch -++ [master^2] Some work. -++ [master^] Some fun. +-- [master] Merge work in mybranch ++* [master^2] Some work. ++* [master^] Some fun. ------------ Remember, before running `git merge`, our `master` head was at @@ -1223,8 +1224,8 @@ $ git show-branch ! [mybranch] Some work. -- + [mybranch] Some work. -+ [master] Some fun. -++ [mybranch^] New day. +* [master] Some fun. +*+ [mybranch^] New day. ------------ Now we are ready to experiment with the merge by hand. @@ -1743,8 +1744,8 @@ $ git show-branch + [diff-fix] Fix rename detection. + [diff-fix~1] Better common substring algorithm. + [commit-fix] Fix commit message normalization. - + [master] Release candidate #1 -+++ [diff-fix~2] Pretty-print messages. + * [master] Release candidate #1 +++* [diff-fix~2] Pretty-print messages. ------------ Both fixes are tested well, and at this point, you want to merge @@ -1764,13 +1765,13 @@ $ git show-branch ! [diff-fix] Fix rename detection. * [master] Merge fix in commit-fix --- - + [master] Merge fix in commit-fix -+ + [commit-fix] Fix commit message normalization. - + [master~1] Merge fix in diff-fix - ++ [diff-fix] Fix rename detection. - ++ [diff-fix~1] Better common substring algorithm. - + [master~2] Release candidate #1 -+++ [master~3] Pretty-print messages. + - [master] Merge fix in commit-fix ++ * [commit-fix] Fix commit message normalization. + - [master~1] Merge fix in diff-fix + +* [diff-fix] Fix rename detection. + +* [diff-fix~1] Better common substring algorithm. + * [master~2] Release candidate #1 +++* [master~3] Pretty-print messages. ------------ However, there is no particular reason to merge in one branch @@ -1797,12 +1798,12 @@ $ git show-branch ! [diff-fix] Fix rename detection. * [master] Octopus merge of branches 'diff-fix' and 'commit-fix' --- - + [master] Octopus merge of branches 'diff-fix' and 'commit-fix' -+ + [commit-fix] Fix commit message normalization. - ++ [diff-fix] Fix rename detection. - ++ [diff-fix~1] Better common substring algorithm. - + [master~1] Release candidate #1 -+++ [master~2] Pretty-print messages. + - [master] Octopus merge of branches 'diff-fix' and 'commit-fix' ++ * [commit-fix] Fix commit message normalization. + +* [diff-fix] Fix rename detection. + +* [diff-fix~1] Better common substring algorithm. + * [master~1] Release candidate #1 +++* [master~2] Pretty-print messages. ------------ Note that you should not do Octopus because you can. An octopus @@ -65,9 +65,11 @@ CFLAGS = -g -O2 -Wall LDFLAGS = ALL_CFLAGS = $(CFLAGS) ALL_LDFLAGS = $(LDFLAGS) +STRIP ?= strip prefix = $(HOME) bindir = $(prefix)/bin +gitexecdir = $(prefix)/bin template_dir = $(prefix)/share/git-core/templates/ GIT_PYTHON_DIR = $(prefix)/share/git-core/python # DESTDIR= @@ -112,14 +114,14 @@ SCRIPT_PYTHON = \ SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \ $(patsubst %.perl,%,$(SCRIPT_PERL)) \ $(patsubst %.py,%,$(SCRIPT_PYTHON)) \ - gitk git-cherry-pick + git-cherry-pick # The ones that do not have to link with lcrypto nor lz. SIMPLE_PROGRAMS = \ git-get-tar-commit-id$X git-mailinfo$X git-mailsplit$X \ git-stripspace$X git-daemon$X -# ... and all the rest +# ... and all the rest that could be moved out of bindir to gitexecdir PROGRAMS = \ git-apply$X git-cat-file$X \ git-checkout-index$X git-clone-pack$X git-commit-tree$X \ @@ -140,8 +142,8 @@ PROGRAMS = \ git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \ git-describe$X -# what 'all' will build and 'install' will install. -ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) git$X +# what 'all' will build and 'install' will install, in gitexecdir +ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) # Backward compatibility -- to be removed after 1.0 PROGRAMS += git-ssh-pull$X git-ssh-push$X @@ -173,7 +175,7 @@ DIFF_OBJS = \ LIB_OBJS = \ blob.o commit.o connect.o count-delta.o csum-file.o \ - date.o diff-delta.o entry.o ident.o index.o \ + date.o diff-delta.o entry.o exec_cmd.o ident.o index.o \ object.o pack-check.o patch-delta.o path.o pkt-line.o \ quote.o read-cache.o refs.o run-command.o \ server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \ @@ -366,13 +368,16 @@ LIB_OBJS += $(COMPAT_OBJS) export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir ### Build rules -all: $(ALL_PROGRAMS) +all: $(ALL_PROGRAMS) git$X gitk all: $(MAKE) -C templates +strip: $(PROGRAMS) git$X + $(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X + git$X: git.c $(LIB_FILE) - $(CC) -DGIT_EXEC_PATH='"$(bindir)"' -DGIT_VERSION='"$(GIT_VERSION)"' \ + $(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \ $(CFLAGS) $(COMPAT_CFLAGS) -o $@ $(filter %.c,$^) $(LIB_FILE) $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh @@ -412,6 +417,8 @@ git$X git.spec \ %.o: %.S $(CC) -o $*.o -c $(ALL_CFLAGS) $< +exec_cmd.o: ALL_CFLAGS += -DGIT_EXEC_PATH=\"$(gitexecdir)\" + git-%$X: %.o $(LIB_FILE) $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) @@ -468,7 +475,9 @@ check: install: all $(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(bindir)) - $(INSTALL) $(ALL_PROGRAMS) $(call shellquote,$(DESTDIR)$(bindir)) + $(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(gitexecdir)) + $(INSTALL) $(ALL_PROGRAMS) $(call shellquote,$(DESTDIR)$(gitexecdir)) + $(INSTALL) git$X gitk $(call shellquote,$(DESTDIR)$(bindir)) $(MAKE) -C templates install $(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(GIT_PYTHON_DIR)) $(INSTALL) $(PYMODULES) $(call shellquote,$(DESTDIR)$(GIT_PYTHON_DIR)) @@ -502,8 +511,7 @@ rpm: dist clean: rm -f *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o $(LIB_FILE) - rm -f $(PROGRAMS) $(SIMPLE_PROGRAMS) git$X - rm -f $(filter-out gitk,$(SCRIPTS)) + rm -f $(ALL_PROGRAMS) git$X rm -f *.spec *.pyc *.pyo */*.pyc */*.pyo rm -rf $(GIT_TARNAME) rm -f $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz @@ -512,6 +520,6 @@ clean: $(MAKE) -C t/ clean rm -f GIT-VERSION-FILE -.PHONY: all install clean +.PHONY: all install clean strip .PHONY: .FORCE-GIT-VERSION-FILE @@ -9,6 +9,7 @@ #include <syslog.h> #include "pkt-line.h" #include "cache.h" +#include "exec_cmd.h" static int log_syslog; static int verbose; @@ -227,7 +228,7 @@ static int upload(char *dir) snprintf(timeout_buf, sizeof timeout_buf, "--timeout=%u", timeout); /* git-upload-pack only ever reads stuff, so this is safe */ - execlp("git-upload-pack", "git-upload-pack", "--strict", timeout_buf, ".", NULL); + execl_git_cmd("upload-pack", "--strict", timeout_buf, ".", NULL); return -1; } diff --git a/describe.c b/describe.c index cc95eb0f21..4866510eaf 100644 --- a/describe.c +++ b/describe.c @@ -98,12 +98,20 @@ static int compare_names(const void *_a, const void *_b) return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1; } -static void describe(struct commit *cmit, int last_one) +static void describe(char *arg, int last_one) { + unsigned char sha1[20]; + struct commit *cmit; struct commit_list *list; static int initialized = 0; struct commit_name *n; + if (get_sha1(arg, sha1) < 0) + usage(describe_usage); + cmit = lookup_commit_reference(sha1); + if (!cmit) + usage(describe_usage); + if (!initialized) { initialized = 1; for_each_ref(get_name); @@ -138,29 +146,29 @@ int main(int argc, char **argv) for (i = 1; i < argc; i++) { const char *arg = argv[i]; - unsigned char sha1[20]; - struct commit *cmit; - if (!strcmp(arg, "--all")) { + if (*arg != '-') + break; + else if (!strcmp(arg, "--all")) all = 1; - continue; - } - if (!strcmp(arg, "--tags")) { + else if (!strcmp(arg, "--tags")) tags = 1; - continue; - } - if (!strncmp(arg, "--abbrev=", 9)) { + else if (!strncmp(arg, "--abbrev=", 9)) { abbrev = strtoul(arg + 9, NULL, 10); if (abbrev < 4 || 40 <= abbrev) abbrev = DEFAULT_ABBREV; - continue; } - if (get_sha1(arg, sha1) < 0) - usage(describe_usage); - cmit = lookup_commit_reference(sha1); - if (!cmit) + else usage(describe_usage); - describe(cmit, i == argc - 1); } + + if (i == argc) + describe("HEAD", 1); + else + while (i < argc) { + describe(argv[i], (i == argc - 1)); + i++; + } + return 0; } diff --git a/exec_cmd.c b/exec_cmd.c new file mode 100644 index 0000000000..55af33bb7e --- /dev/null +++ b/exec_cmd.c @@ -0,0 +1,117 @@ +#include "cache.h" +#include "exec_cmd.h" +#define MAX_ARGS 32 + +extern char **environ; +static const char *builtin_exec_path = GIT_EXEC_PATH; +static const char *current_exec_path = NULL; + +void git_set_exec_path(const char *exec_path) +{ + current_exec_path = exec_path; +} + + +/* Returns the highest-priority, location to look for git programs. */ +const char *git_exec_path() +{ + const char *env; + + if (current_exec_path) + return current_exec_path; + + env = getenv("GIT_EXEC_PATH"); + if (env) { + return env; + } + + return builtin_exec_path; +} + + +int execv_git_cmd(char **argv) +{ + char git_command[PATH_MAX + 1]; + char *tmp; + int len, err, i; + const char *paths[] = { current_exec_path, + getenv("GIT_EXEC_PATH"), + builtin_exec_path }; + + for (i = 0; i < sizeof(paths)/sizeof(paths[0]); ++i) { + const char *exec_dir = paths[i]; + if (!exec_dir) continue; + + if (*exec_dir != '/') { + if (!getcwd(git_command, sizeof(git_command))) { + fprintf(stderr, "git: cannot determine " + "current directory\n"); + exit(1); + } + len = strlen(git_command); + + /* Trivial cleanup */ + while (!strncmp(exec_dir, "./", 2)) { + exec_dir += 2; + while (*exec_dir == '/') + exec_dir++; + } + snprintf(git_command + len, sizeof(git_command) - len, + "/%s", exec_dir); + } else { + strcpy(git_command, exec_dir); + } + + len = strlen(git_command); + len += snprintf(git_command + len, sizeof(git_command) - len, + "/git-%s", argv[0]); + + if (sizeof(git_command) <= len) { + fprintf(stderr, + "git: command name given is too long.\n"); + break; + } + + /* argv[0] must be the git command, but the argv array + * belongs to the caller, and my be reused in + * subsequent loop iterations. Save argv[0] and + * restore it on error. + */ + + tmp = argv[0]; + argv[0] = git_command; + + /* execve() can only ever return if it fails */ + execve(git_command, argv, environ); + + err = errno; + + argv[0] = tmp; + } + return -1; + +} + + +int execl_git_cmd(char *cmd,...) +{ + int argc; + char *argv[MAX_ARGS + 1]; + char *arg; + va_list param; + + va_start(param, cmd); + argv[0] = cmd; + argc = 1; + while (argc < MAX_ARGS) { + arg = argv[argc++] = va_arg(param, char *); + if (!arg) + break; + } + va_end(param); + if (MAX_ARGS <= argc) + return error("too many args to run %s", cmd); + + argv[argc] = NULL; + return execv_git_cmd(argv); +} diff --git a/exec_cmd.h b/exec_cmd.h new file mode 100644 index 0000000000..5150ee29f7 --- /dev/null +++ b/exec_cmd.h @@ -0,0 +1,10 @@ +#ifndef __GIT_EXEC_CMD_H_ +#define __GIT_EXEC_CMD_H_ + +extern void git_set_exec_path(const char *exec_path); +extern const char* git_exec_path(void); +extern int execv_git_cmd(char **argv); /* NULL terminated */ +extern int execl_git_cmd(char *cmd, ...); + + +#endif /* __GIT_EXEC_CMD_H_ */ diff --git a/fetch-clone.c b/fetch-clone.c index f46fe6ecbb..859f400941 100644 --- a/fetch-clone.c +++ b/fetch-clone.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "exec_cmd.h" #include <sys/wait.h> static int finish_pack(const char *pack_tmp_name, const char *me) @@ -27,8 +28,7 @@ static int finish_pack(const char *pack_tmp_name, const char *me) dup2(pipe_fd[1], 1); close(pipe_fd[0]); close(pipe_fd[1]); - execlp("git-index-pack","git-index-pack", - "-o", idx, pack_tmp_name, NULL); + execl_git_cmd("index-pack", "-o", idx, pack_tmp_name, NULL); error("cannot exec git-index-pack <%s> <%s>", idx, pack_tmp_name); exit(1); @@ -105,8 +105,7 @@ int receive_unpack_pack(int fd[2], const char *me, int quiet) dup2(fd[0], 0); close(fd[0]); close(fd[1]); - execlp("git-unpack-objects", "git-unpack-objects", - quiet ? "-q" : NULL, NULL); + execl_git_cmd("unpack-objects", quiet ? "-q" : NULL, NULL); die("git-unpack-objects exec failed"); } close(fd[0]); diff --git a/git-checkout.sh b/git-checkout.sh index 3bbd111773..d99688fbf2 100755 --- a/git-checkout.sh +++ b/git-checkout.sh @@ -1,6 +1,6 @@ #!/bin/sh -USAGE='[-f] [-b <new_branch>] [<branch>] [<paths>...]' +USAGE='[-f] [-b <new_branch>] [-m] [<branch>] [<paths>...]' SUBDIRECTORY_OK=Sometimes . git-sh-setup @@ -9,6 +9,7 @@ new= force= branch= newbranch= +merge= while [ "$#" != "0" ]; do arg="$1" shift @@ -26,6 +27,9 @@ while [ "$#" != "0" ]; do "-f") force=1 ;; + -m) + merge=1 + ;; --) break ;; @@ -71,7 +75,7 @@ done if test "$#" -ge 1 then - if test '' != "$newbranch$force" + if test '' != "$newbranch$force$merge" then die "updating paths and switching branches or forcing are incompatible." fi @@ -121,7 +125,48 @@ then git-checkout-index -q -f -u -a else git-update-index --refresh >/dev/null - git-read-tree -m -u $old $new + merge_error=$(git-read-tree -m -u $old $new 2>&1) || ( + case "$merge" in + '') + echo >&2 "$merge_error" + exit 1 ;; + esac + + # Match the index to the working tree, and do a three-way. + git diff-files --name-only | git update-index --remove --stdin && + work=`git write-tree` && + git read-tree --reset $new && + git checkout-index -f -u -q -a && + git read-tree -m -u $old $new $work || exit + + if result=`git write-tree 2>/dev/null` + then + echo >&2 "Trivially automerged." + else + git merge-index -o git-merge-one-file -a + fi + + # Do not register the cleanly merged paths in the index yet. + # this is not a real merge before committing, but just carrying + # the working tree changes along. + unmerged=`git ls-files -u` + git read-tree --reset $new + case "$unmerged" in + '') ;; + *) + ( + z40=0000000000000000000000000000000000000000 + echo "$unmerged" | + sed -e 's/^[0-7]* [0-9a-f]* /'"0 $z40 /" + echo "$unmerged" + ) | git update-index --index-info + ;; + esac + exit 0 + ) + saved_err=$? + git diff-files --name-status + (exit $saved_err) fi # diff --git a/git-clone.sh b/git-clone.sh index 377d59e62c..168eb963ba 100755 --- a/git-clone.sh +++ b/git-clone.sh @@ -9,7 +9,7 @@ unset CDPATH usage() { - echo >&2 "Usage: $0 [-l [-s]] [-q] [-u <upload-pack>] [-o <name>] [-n] <repo> [<dir>]" + echo >&2 "Usage: $0 [--naked] [-l [-s]] [-q] [-u <upload-pack>] [-o <name>] [-n] <repo> [<dir>]" exit 1 } @@ -53,11 +53,15 @@ use_local=no local_shared=no no_checkout= upload_pack= +naked= origin=origin while case "$#,$1" in 0,*) break ;; - *,-n) no_checkout=yes ;; + *,-n|*,--no|*,--no-|*,--no-c|*,--no-ch|*,--no-che|*,--no-chec|\ + *,--no-check|*,--no-checko|*,--no-checkou|*,--no-checkout) + no_checkout=yes ;; + *,--na|*,--nak|*,--nake|*,--naked) naked=yes ;; *,-l|*,--l|*,--lo|*,--loc|*,--loca|*,--local) use_local=yes ;; *,-s|*,--s|*,--sh|*,--sha|*,--shar|*,--share|*,--shared) local_shared=yes; use_local=yes ;; @@ -81,6 +85,9 @@ do shift done +# --naked implies --no-checkout +test -z "$naked" || no_checkout=yes + # Turn the source into an absolute path if # it is local repo="$1" @@ -95,10 +102,17 @@ dir="$2" [ -z "$dir" ] && dir=$(echo "$repo" | sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*/||g') [ -e "$dir" ] && echo "$dir already exists." && usage mkdir -p "$dir" && -D=$( - (cd "$dir" && git-init-db && pwd) -) && -test -d "$D" || usage +D=$(cd "$dir" && pwd) && +case "$naked" in +yes) GIT_DIR="$D" ;; +*) GIT_DIR="$D/.git" ;; +esac && export GIT_DIR && git-init-db || usage +case "$naked" in +yes) + GIT_DIR="$D" ;; +*) + GIT_DIR="$D/.git" ;; +esac # We do local magic only when the user tells us to. case "$local,$use_local" in @@ -118,21 +132,21 @@ yes,yes) test -f "$repo/$sample_file" || exit l= - if ln "$repo/$sample_file" "$D/.git/objects/sample" 2>/dev/null + if ln "$repo/$sample_file" "$GIT_DIR/objects/sample" 2>/dev/null then l=l fi && - rm -f "$D/.git/objects/sample" && + rm -f "$GIT_DIR/objects/sample" && cd "$repo" && - find objects -depth -print | cpio -puamd$l "$D/.git/" || exit 1 + find objects -depth -print | cpio -puamd$l "$GIT_DIR/" || exit 1 ;; yes) - mkdir -p "$D/.git/objects/info" + mkdir -p "$GIT_DIR/objects/info" { test -f "$repo/objects/info/alternates" && cat "$repo/objects/info/alternates"; echo "$repo/objects" - } >"$D/.git/objects/info/alternates" + } >"$GIT_DIR/objects/info/alternates" ;; esac @@ -143,27 +157,27 @@ yes,yes) HEAD=HEAD fi (cd "$repo" && tar cf - refs $HEAD) | - (cd "$D/.git" && tar xf -) || exit 1 + (cd "$GIT_DIR" && tar xf -) || exit 1 ;; *) case "$repo" in rsync://*) rsync $quiet -av --ignore-existing \ - --exclude info "$repo/objects/" "$D/.git/objects/" && + --exclude info "$repo/objects/" "$GIT_DIR/objects/" && rsync $quiet -av --ignore-existing \ - --exclude info "$repo/refs/" "$D/.git/refs/" || exit + --exclude info "$repo/refs/" "$GIT_DIR/refs/" || exit # Look at objects/info/alternates for rsync -- http will # support it natively and git native ones will do it on the # remote end. Not having that file is not a crime. rsync -q "$repo/objects/info/alternates" \ - "$D/.git/TMP_ALT" 2>/dev/null || - rm -f "$D/.git/TMP_ALT" - if test -f "$D/.git/TMP_ALT" + "$GIT_DIR/TMP_ALT" 2>/dev/null || + rm -f "$GIT_DIR/TMP_ALT" + if test -f "$GIT_DIR/TMP_ALT" then ( cd "$D" && . git-parse-remote && - resolve_alternates "$repo" <"./.git/TMP_ALT" ) | + resolve_alternates "$repo" <"$GIT_DIR/TMP_ALT" ) | while read alt do case "$alt" in 'bad alternate: '*) die "$alt";; esac @@ -171,9 +185,9 @@ yes,yes) '') echo >&2 "Getting alternate: $alt" ;; esac rsync $quiet -av --ignore-existing \ - --exclude info "$alt" "$D/.git/objects" || exit + --exclude info "$alt" "$GIT_DIR/objects" || exit done - rm -f "$D/.git/TMP_ALT" + rm -f "$GIT_DIR/TMP_ALT" fi ;; http://*) @@ -194,25 +208,25 @@ esac cd "$D" || exit -if test -f ".git/HEAD" +if test -f "$GIT_DIR/HEAD" then head_points_at=`git-symbolic-ref HEAD` case "$head_points_at" in refs/heads/*) head_points_at=`expr "$head_points_at" : 'refs/heads/\(.*\)'` - mkdir -p .git/remotes && - echo >.git/remotes/origin \ + mkdir -p "$GIT_DIR/remotes" && + echo >"$GIT_DIR/remotes/origin" \ "URL: $repo Pull: $head_points_at:$origin" && git-update-ref "refs/heads/$origin" $(git-rev-parse HEAD) && - find .git/refs/heads -type f -print | + (cd "$GIT_DIR" && find "refs/heads" -type f -print) | while read ref do - head=`expr "$ref" : '.git/refs/heads/\(.*\)'` && + head=`expr "$ref" : 'refs/heads/\(.*\)'` && test "$head_points_at" = "$head" || test "$origin" = "$head" || echo "Pull: ${head}:${head}" - done >>.git/remotes/origin + done >>"$GIT_DIR/remotes/origin" esac case "$no_checkout" in diff --git a/git-cvsimport.perl b/git-cvsimport.perl index 8619e7d183..fc207fc47e 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -29,19 +29,63 @@ use IPC::Open2; $SIG{'PIPE'}="IGNORE"; $ENV{'TZ'}="UTC"; -our($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M); +our($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M,$opt_A); +my (%conv_author_name, %conv_author_email); sub usage() { print STDERR <<END; Usage: ${\basename $0} # fetch/update GIT from CVS - [-o branch-for-HEAD] [-h] [-v] [-d CVSROOT] - [-p opts-for-cvsps] [-C GIT_repository] [-z fuzz] - [-i] [-k] [-u] [-s subst] [-m] [-M regex] [CVS_module] + [-o branch-for-HEAD] [-h] [-v] [-d CVSROOT] [-A author-conv-file] + [-p opts-for-cvsps] [-C GIT_repository] [-z fuzz] [-i] [-k] [-u] + [-s subst] [-m] [-M regex] [CVS_module] END exit(1); } -getopts("hivmkuo:d:p:C:z:s:M:P:") or usage(); +sub read_author_info($) { + my ($file) = @_; + my $user; + open my $f, '<', "$file" or die("Failed to open $file: $!\n"); + + while (<$f>) { + # Expected format is this: + # exon=Andreas Ericsson <ae@op5.se> + if (m/^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/) { + $user = $1; + $conv_author_name{$user} = $2; + $conv_author_email{$user} = $3; + } + # However, we also read from CVSROOT/users format + # to ease migration. + elsif (/^(\w+):(['"]?)(.+?)\2\s*$/) { + my $mapped; + ($user, $mapped) = ($1, $3); + if ($mapped =~ /^\s*(.*?)\s*<(.*)>\s*$/) { + $conv_author_name{$user} = $1; + $conv_author_email{$user} = $2; + } + elsif ($mapped =~ /^<?(.*)>?$/) { + $conv_author_name{$user} = $user; + $conv_author_email{$user} = $1; + } + } + # NEEDSWORK: Maybe warn on unrecognized lines? + } + close ($f); +} + +sub write_author_info($) { + my ($file) = @_; + open my $f, '>', $file or + die("Failed to open $file for writing: $!"); + + foreach (keys %conv_author_name) { + print $f "$_=$conv_author_name{$_} <$conv_author_email{$_}>\n"; + } + close ($f); +} + +getopts("hivmkuo:d:p:C:z:s:M:P:A:") or usage(); usage if $opt_h; @ARGV <= 1 or usage(); @@ -453,7 +497,7 @@ CVS2GIT_HEAD exists. Make sure your working directory corresponds to HEAD and remove CVS2GIT_HEAD. You may need to run - git-read-tree -m -u CVS2GIT_HEAD HEAD + git read-tree -m -u CVS2GIT_HEAD HEAD EOM } system('cp', "$git_dir/HEAD", "$git_dir/CVS2GIT_HEAD"); @@ -489,6 +533,14 @@ EOM -d $git_dir or die "Could not create git subdir ($git_dir).\n"; +# now we read (and possibly save) author-info as well +-f "$git_dir/cvs-authors" and + read_author_info("$git_dir/cvs-authors"); +if ($opt_A) { + read_author_info($opt_A); + write_author_info("$git_dir/cvs-authors"); +} + my $pid = open(CVS,"-|"); die "Cannot fork: $!\n" unless defined $pid; unless($pid) { @@ -702,6 +754,9 @@ while(<CVS>) { s/\s+$//; if (/^(.*?)\s+<(.*)>/) { ($author_name, $author_email) = ($1, $2); + } elsif ($conv_author_name{$_}) { + $author_name = $conv_author_name{$_}; + $author_email = $conv_author_email{$_}; } else { $author_name = $author_email = $_; } diff --git a/git-fetch.sh b/git-fetch.sh index 73e57bd784..4a0cb32f30 100755 --- a/git-fetch.sh +++ b/git-fetch.sh @@ -40,6 +40,9 @@ do -v|--verbose) verbose=Yes ;; + -k|--k|--ke|--kee|--keep) + keep=--keep + ;; -*) usage ;; @@ -309,7 +312,7 @@ fetch_main () { ( : subshell because we muck with IFS IFS=" $LF" ( - git-fetch-pack "$remote" $rref || echo failed "$remote" + git-fetch-pack $keep "$remote" $rref || echo failed "$remote" ) | while read sha1 remote_name do diff --git a/git-format-patch.sh b/git-format-patch.sh index d3979d7631..7e67c4e403 100755 --- a/git-format-patch.sh +++ b/git-format-patch.sh @@ -34,14 +34,12 @@ outdir=./ while case "$#" in 0) break;; esac do case "$1" in - -a|--a|--au|--aut|--auth|--autho|--author) - author=t ;; -c|--c|--ch|--che|--chec|--check) check=t ;; - -d|--d|--da|--dat|--date) - date=t ;; - -m|--m|--mb|--mbo|--mbox) - date=t author=t mbox=t ;; + -a|--a|--au|--aut|--auth|--autho|--author|\ + -d|--d|--da|--dat|--date|\ + -m|--m|--mb|--mbo|--mbox) # now noop + ;; -k|--k|--ke|--kee|--keep|--keep-|--keep-s|--keep-su|--keep-sub|\ --keep-subj|--keep-subje|--keep-subjec|--keep-subject) keep_subject=t ;; @@ -173,80 +171,89 @@ titleScript=' q ' -whosepatchScript=' -/^author /{ - s/'\''/'\''\\'\'\''/g - s/author \(.*>\) \(.*\)$/au='\''\1'\'' ad='\''\2'\''/p - q -}' - process_one () { - mailScript=' - /./d - /^$/n' - case "$keep_subject" in - t) ;; - *) - mailScript="$mailScript"' - s|^\[PATCH[^]]*\] *|| - s|^|[PATCH'"$num"'] |' - ;; - esac - mailScript="$mailScript"' - s|^|Subject: |' - case "$mbox" in - t) - echo 'From nobody Mon Sep 17 00:00:00 2001' ;# UNIX "From" line - ;; - esac + perl -w -e ' +my ($keep_subject, $num, $signoff, $commsg) = @ARGV; +my ($signoff_pattern, $done_header, $done_subject, $signoff_seen, + $last_was_signoff); - eval "$(sed -ne "$whosepatchScript" $commsg)" - test "$author,$au" = ",$me" || { - mailScript="$mailScript"' - a\ -From: '"$au" - } - test "$date,$au" = ",$me" || { - mailScript="$mailScript"' - a\ -Date: '"$ad" - } +if ($signoff) { + $signoff = `git-var GIT_COMMITTER_IDENT`; + $signoff =~ s/>.*/>/; + $signoff_pattern = quotemeta($signoff); +} - mailScript="$mailScript"' - a\ +my @weekday_names = qw(Sun Mon Tue Wed Thu Fri Sat); +my @month_names = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); - : body - p - n - b body' +sub show_date { + my ($time, $tz) = @_; + my $minutes = abs($tz); + $minutes = ($minutes / 100) * 60 + ($minutes % 100); + if ($tz < 0) { + $minutes = -$minutes; + } + my $t = $time + $minutes * 60; + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime($t); + return sprintf("%s %s %d %02d:%02d:%02d %d %+05d", + $weekday_names[$wday], + $month_names[$mon], + $mday, $hour, $min, $sec, + $year+1900, $tz); +} - (cat $commsg ; echo; echo) | - sed -ne "$mailScript" | - git-stripspace +print "From nobody Mon Sep 17 00:00:00 2001\n"; +open FH, "git stripspace <$commsg |" or die "open $commsg pipe"; +while (<FH>) { + unless ($done_header) { + if (/^$/) { + $done_header = 1; + } + elsif (/^author (.*>) (.*)$/) { + my ($author_ident, $author_date) = ($1, $2); + my ($utc, $off) = ($author_date =~ /^(\d+) ([-+]?\d+)$/); + $author_date = show_date($utc, $off); - test "$signoff" = "t" && { - offsigner=`git-var GIT_COMMITTER_IDENT | sed -e 's/>.*/>/'` - line="Signed-off-by: $offsigner" - grep -q "^$line\$" $commsg || { - echo - echo "$line" - echo - } + print "From: $author_ident\n"; + print "Date: $author_date\n"; } - echo - echo '---' - echo + next; + } + unless ($done_subject) { + unless ($keep_subject) { + s/^\[PATCH[^]]*\]\s*//; + s/^/[PATCH$num] /; + } + print "Subject: $_"; + $done_subject = 1; + next; + } + + $last_was_signoff = 0; + if (/Signed-off-by:/i) { + if ($signoff ne "" && /Signed-off-by:\s*$signoff_pattern$/i) { + $signoff_seen = 1; + } + } + print $_; +} +if (!$signoff_seen && $signoff ne "") { + if (!$last_was_signoff) { + print "\n"; + } + print "$signoff\n"; +} +print "\n---\n\n"; +close FH or die "close $commsg pipe"; +' "$keep_subject" "$num" "$signoff" $commsg + git-diff-tree -p $diff_opts "$commit" | git-apply --stat --summary echo git-diff-tree -p $diff_opts "$commit" echo "-- " echo "@@GIT_VERSION@@" - case "$mbox" in - t) - echo - ;; - esac + echo } total=`wc -l <$series | tr -dc "[0-9]"` diff --git a/git-merge-octopus.sh b/git-merge-octopus.sh index 7adffdc795..eb74f96e81 100755 --- a/git-merge-octopus.sh +++ b/git-merge-octopus.sh @@ -48,16 +48,24 @@ MRC=$head MSG= PARENT="-p $head" MRT=$(git-write-tree) CNT=1 ;# counting our head NON_FF_MERGE=0 +OCTOPUS_FAILURE=0 for SHA1 in $remotes do + case "$OCTOPUS_FAILURE" in + 1) + # We allow only last one to have a hand-resolvable + # conflicts. Last round failed and we still had + # a head to merge. + echo "Automated merge did not work." + echo "Should not be doing an Octopus." + exit 2 + esac + common=$(git-merge-base --all $MRC $SHA1) || die "Unable to find common commit with $SHA1" - case "$common" in - ?*"$LF"?*) - die "Not trivially mergeable." - ;; - $SHA1) + case "$LF$common$LF" in + *"$LF$SHA1$LF"*) echo "Already up-to-date with $SHA1" continue ;; @@ -88,7 +96,7 @@ do then echo "Simple merge did not work, trying automatic merge." git-merge-index -o git-merge-one-file -a || - exit 2 ; # Automatic merge failed; should not be doing Octopus + OCTOPUS_FAILURE=1 next=$(git-write-tree 2>/dev/null) fi @@ -103,4 +111,4 @@ do MRT=$next done -exit 0 +exit "$OCTOPUS_FAILURE" diff --git a/git-push.sh b/git-push.sh index 1c5cf80f87..136093bf13 100755 --- a/git-push.sh +++ b/git-push.sh @@ -9,12 +9,15 @@ has_all= has_force= has_exec= remote= +do_tags= while case "$#" in 0) break ;; esac do case "$1" in --all) has_all=--all ;; + --tags) + do_tags=yes ;; --force) has_force=--force ;; --exec=*) @@ -33,6 +36,10 @@ case "$#" in echo "Where would you want to push today?" usage ;; esac +if test ",$has_all,$do_tags," = ",--all,yes," +then + do_tags= +fi . git-parse-remote remote=$(get_remote_url "$@") @@ -42,6 +49,20 @@ case "$has_all" in esac shift +case "$do_tags" in +yes) + set "$@" $(cd "$GIT_DIR/refs" && find tags -type f -print) ;; +esac + +# Now we have explicit refs from the command line or from remotes/ +# shorthand, or --tags. Falling back on the current branch if we still +# do not have any may be an alternative, but prevent mistakes for now. + +case "$#,$has_all" in +0,) + die "No refs given to be pushed." ;; +esac + case "$remote" in git://*) die "Cannot use READ-ONLY transport to push to $remote" ;; @@ -10,6 +10,7 @@ #include <stdarg.h> #include <sys/ioctl.h> #include "git-compat-util.h" +#include "exec_cmd.h" #ifndef PATH_MAX # define PATH_MAX 4096 @@ -233,14 +234,11 @@ int main(int argc, char **argv, char **envp) { char git_command[PATH_MAX + 1]; char wd[PATH_MAX + 1]; - int i, len, show_help = 0; - char *exec_path = getenv("GIT_EXEC_PATH"); + int i, show_help = 0; + const char *exec_path; getcwd(wd, PATH_MAX); - if (!exec_path) - exec_path = GIT_EXEC_PATH; - for (i = 1; i < argc; i++) { char *arg = argv[i]; @@ -256,10 +254,11 @@ int main(int argc, char **argv, char **envp) if (!strncmp(arg, "exec-path", 9)) { arg += 9; - if (*arg == '=') + if (*arg == '=') { exec_path = arg + 1; - else { - puts(exec_path); + git_set_exec_path(exec_path); + } else { + puts(git_exec_path()); exit(0); } } @@ -275,42 +274,15 @@ int main(int argc, char **argv, char **envp) if (i >= argc || show_help) { if (i >= argc) - cmd_usage(exec_path, NULL); + cmd_usage(git_exec_path(), NULL); show_man_page(argv[i]); } - if (*exec_path != '/') { - if (!getcwd(git_command, sizeof(git_command))) { - fprintf(stderr, - "git: cannot determine current directory\n"); - exit(1); - } - len = strlen(git_command); - - /* Trivial cleanup */ - while (!strncmp(exec_path, "./", 2)) { - exec_path += 2; - while (*exec_path == '/') - exec_path++; - } - snprintf(git_command + len, sizeof(git_command) - len, - "/%s", exec_path); - } - else - strcpy(git_command, exec_path); - len = strlen(git_command); - prepend_to_path(git_command, len); - - len += snprintf(git_command + len, sizeof(git_command) - len, - "/git-%s", argv[i]); - if (sizeof(git_command) <= len) { - fprintf(stderr, "git: command name given is too long.\n"); - exit(1); - } + exec_path = git_exec_path(); + prepend_to_path(exec_path, strlen(exec_path)); - /* execve() can only ever return if it fails */ - execve(git_command, &argv[i], envp); + execv_git_cmd(argv + i); if (errno == ENOENT) cmd_usage(exec_path, "'%s' is not a git-command", argv[i]); diff --git a/receive-pack.c b/receive-pack.c index f847ec2b54..eae31e370d 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -6,7 +6,7 @@ static const char receive_pack_usage[] = "git-receive-pack <git-dir>"; -static const char unpacker[] = "git-unpack-objects"; +static char *unpacker[] = { "unpack-objects", NULL }; static int report_status = 0; @@ -257,7 +257,7 @@ static void read_head_info(void) static const char *unpack(int *error_code) { - int code = run_command(unpacker, NULL); + int code = run_command_v_opt(1, unpacker, RUN_GIT_CMD); *error_code = 0; switch (code) { diff --git a/run-command.c b/run-command.c index 8bf5922fc3..b3d287e97e 100644 --- a/run-command.c +++ b/run-command.c @@ -1,6 +1,7 @@ #include "cache.h" #include "run-command.h" #include <sys/wait.h> +#include "exec_cmd.h" int run_command_v_opt(int argc, char **argv, int flags) { @@ -13,9 +14,13 @@ int run_command_v_opt(int argc, char **argv, int flags) int fd = open("/dev/null", O_RDWR); dup2(fd, 0); dup2(fd, 1); - close(fd); + close(fd); + } + if (flags & RUN_GIT_CMD) { + execv_git_cmd(argv); + } else { + execvp(argv[0], (char *const*) argv); } - execvp(argv[0], (char *const*) argv); die("exec %s failed.", argv[0]); } for (;;) { diff --git a/run-command.h b/run-command.h index 2469eeaefb..ef3ee053de 100644 --- a/run-command.h +++ b/run-command.h @@ -12,7 +12,7 @@ enum { }; #define RUN_COMMAND_NO_STDIO 1 - +#define RUN_GIT_CMD 2 /*If this is to be git sub-command */ int run_command_v_opt(int argc, char **argv, int opt); int run_command_v(int argc, char **argv); int run_command(const char *cmd, ...); diff --git a/send-pack.c b/send-pack.c index cd36193444..990be3f1a3 100644 --- a/send-pack.c +++ b/send-pack.c @@ -3,6 +3,7 @@ #include "tag.h" #include "refs.h" #include "pkt-line.h" +#include "exec_cmd.h" static const char send_pack_usage[] = "git-send-pack [--all] [--exec=git-receive-pack] <remote> [<head>...]\n" @@ -26,11 +27,11 @@ static int is_zero_sha1(const unsigned char *sha1) static void exec_pack_objects(void) { static char *args[] = { - "git-pack-objects", + "pack-objects", "--stdout", NULL }; - execvp("git-pack-objects", args); + execv_git_cmd(args); die("git-pack-objects exec failed (%s)", strerror(errno)); } @@ -39,7 +40,7 @@ static void exec_rev_list(struct ref *refs) static char *args[1000]; int i = 0; - args[i++] = "git-rev-list"; /* 0 */ + args[i++] = "rev-list"; /* 0 */ args[i++] = "--objects"; /* 1 */ while (refs) { char *buf = malloc(100); @@ -58,7 +59,7 @@ static void exec_rev_list(struct ref *refs) refs = refs->next; } args[i] = NULL; - execvp("git-rev-list", args); + execv_git_cmd(args); die("git-rev-list exec failed (%s)", strerror(errno)); } @@ -1,5 +1,6 @@ #include "cache.h" #include "quote.h" +#include "exec_cmd.h" static int do_generic_cmd(const char *me, char *arg) { @@ -7,12 +8,14 @@ static int do_generic_cmd(const char *me, char *arg) if (!arg || !(arg = sq_dequote(arg))) die("bad argument"); + if (strncmp(me, "git-", 4)) + die("bad command"); - my_argv[0] = me; + my_argv[0] = me + 4; my_argv[1] = arg; my_argv[2] = NULL; - return execvp(me, (char**) my_argv); + return execv_git_cmd((char**) my_argv); } static struct commands { diff --git a/show-branch.c b/show-branch.c index 1935c448f4..7a0dcc649e 100644 --- a/show-branch.c +++ b/show-branch.c @@ -5,7 +5,11 @@ #include "refs.h" static const char show_branch_usage[] = -"git-show-branch [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [<refs>...]"; +"git-show-branch [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [<refs>...]"; + +static int default_num = 0; +static int default_alloc = 0; +static char **default_arg = NULL; #define UNINTERESTING 01 @@ -431,12 +435,12 @@ static void snarf_refs(int head, int tag) } } -static int rev_is_head(char *head_path, int headlen, - char *name, +static int rev_is_head(char *head_path, int headlen, char *name, unsigned char *head_sha1, unsigned char *sha1) { int namelen; - if ((!head_path[0]) || memcmp(head_sha1, sha1, 20)) + if ((!head_path[0]) || + (head_sha1 && sha1 && memcmp(head_sha1, sha1, 20))) return 0; namelen = strlen(name); if ((headlen < namelen) || @@ -508,6 +512,21 @@ static void append_one_rev(const char *av) die("bad sha1 reference %s", av); } +static int git_show_branch_config(const char *var, const char *value) +{ + if (!strcmp(var, "showbranch.default")) { + if (default_alloc <= default_num + 1) { + default_alloc = default_alloc * 3 / 2 + 20; + default_arg = xrealloc(default_arg, sizeof *default_arg * default_alloc); + } + default_arg[default_num++] = strdup(value); + default_arg[default_num] = NULL; + return 0; + } + + return git_default_config(var, value); +} + int main(int ac, char **av) { struct commit *rev[MAX_REVS], *commit; @@ -526,12 +545,25 @@ int main(int ac, char **av) int sha1_name = 0; int shown_merge_point = 0; int topo_order = 0; + int with_current_branch = 0; + int head_at = -1; + git_config(git_show_branch_config); setup_git_directory(); + /* If nothing is specified, try the default first */ + if (ac == 1 && default_num) { + ac = default_num + 1; + av = default_arg - 1; /* ick; we would not address av[0] */ + } + while (1 < ac && av[1][0] == '-') { char *arg = av[1]; - if (!strcmp(arg, "--all")) + if (!strcmp(arg, "--")) { + ac--; av++; + break; + } + else if (!strcmp(arg, "--all")) all_heads = all_tags = 1; else if (!strcmp(arg, "--heads")) all_heads = 1; @@ -543,6 +575,8 @@ int main(int ac, char **av) extra = -1; else if (!strcmp(arg, "--no-name")) no_name = 1; + else if (!strcmp(arg, "--current")) + with_current_branch = 1; else if (!strcmp(arg, "--sha1-name")) sha1_name = 1; else if (!strncmp(arg, "--more=", 7)) @@ -574,6 +608,34 @@ int main(int ac, char **av) ac--; av++; } + head_path_p = resolve_ref(git_path("HEAD"), head_sha1, 1); + if (head_path_p) { + head_path_len = strlen(head_path_p); + memcpy(head_path, head_path_p, head_path_len + 1); + } + else { + head_path_len = 0; + head_path[0] = 0; + } + + if (with_current_branch && head_path_p) { + int has_head = 0; + for (i = 0; !has_head && i < ref_name_cnt; i++) { + /* We are only interested in adding the branch + * HEAD points at. + */ + if (rev_is_head(head_path, + head_path_len, + ref_name[i], + head_sha1, NULL)) + has_head++; + } + if (!has_head) { + int pfxlen = strlen(git_path("refs/heads/")); + append_one_rev(head_path + pfxlen); + } + } + if (!ref_name_cnt) { fprintf(stderr, "No revs to be shown.\n"); exit(0); @@ -609,16 +671,6 @@ int main(int ac, char **av) if (0 <= extra) join_revs(&list, &seen, num_rev, extra); - head_path_p = resolve_ref(git_path("HEAD"), head_sha1, 1); - if (head_path_p) { - head_path_len = strlen(head_path_p); - memcpy(head_path, head_path_p, head_path_len + 1); - } - else { - head_path_len = 0; - head_path[0] = 0; - } - if (merge_base) return show_merge_base(seen, num_rev); @@ -645,6 +697,8 @@ int main(int ac, char **av) } /* header lines never need name */ show_one_commit(rev[i], 1); + if (is_head) + head_at = i; } if (0 <= extra) { for (i = 0; i < num_rev; i++) @@ -673,9 +727,19 @@ int main(int ac, char **av) shown_merge_point |= ((this_flag & all_revs) == all_revs); if (1 < num_rev) { - for (i = 0; i < num_rev; i++) - putchar((this_flag & (1u << (i + REV_SHIFT))) - ? '+' : ' '); + int is_merge = !!(commit->parents && commit->parents->next); + for (i = 0; i < num_rev; i++) { + int mark; + if (!(this_flag & (1u << (i + REV_SHIFT)))) + mark = ' '; + else if (is_merge) + mark = '-'; + else if (i == head_at) + mark = '*'; + else + mark = '+'; + putchar(mark); + } putchar(' '); } show_one_commit(commit, no_name); diff --git a/t/t1200-tutorial.sh b/t/t1200-tutorial.sh index d7562e9748..8ff5dd92c9 100755 --- a/t/t1200-tutorial.sh +++ b/t/t1200-tutorial.sh @@ -118,8 +118,8 @@ cat > show-branch.expect << EOF * [master] Merged "mybranch" changes. ! [mybranch] Some work. -- -+ [master] Merged "mybranch" changes. -++ [mybranch] Some work. +- [master] Merged "mybranch" changes. +*+ [mybranch] Some work. EOF git show-branch --topo-order master mybranch > show-branch.output @@ -142,7 +142,7 @@ cat > show-branch2.expect << EOF ! [master] Merged "mybranch" changes. * [mybranch] Merged "mybranch" changes. -- -++ [master] Merged "mybranch" changes. +-- [master] Merged "mybranch" changes. EOF git show-branch --topo-order master mybranch > show-branch2.output diff --git a/upload-pack.c b/upload-pack.c index 1834b6ba8c..d1980556ca 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -4,6 +4,7 @@ #include "tag.h" #include "object.h" #include "commit.h" +#include "exec_cmd.h" static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] <dir>"; @@ -60,7 +61,7 @@ static void create_pack_file(void) close(0); close(fd[0]); close(fd[1]); - *p++ = "git-rev-list"; + *p++ = "rev-list"; *p++ = "--objects"; if (create_full_pack || MAX_NEEDS <= nr_needs) *p++ = "--all"; @@ -79,13 +80,13 @@ static void create_pack_file(void) buf += 41; } *p++ = NULL; - execvp("git-rev-list", argv); + execv_git_cmd(argv); die("git-upload-pack: unable to exec git-rev-list"); } dup2(fd[0], 0); close(fd[0]); close(fd[1]); - execlp("git-pack-objects", "git-pack-objects", "--stdout", NULL); + execl_git_cmd("pack-objects", "--stdout", NULL); die("git-upload-pack: unable to exec git-pack-objects"); } |