diff options
-rw-r--r-- | Documentation/git-rebase.txt | 76 | ||||
-rw-r--r-- | cache.h | 1 | ||||
-rwxr-xr-x | git-fetch.sh | 16 | ||||
-rwxr-xr-x | git-rebase.sh | 62 | ||||
-rw-r--r-- | pack-objects.c | 12 | ||||
-rw-r--r-- | revision.c | 14 | ||||
-rw-r--r-- | setup.c | 24 |
7 files changed, 155 insertions, 50 deletions
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 4a7e67a4d2..1b482abecd 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -3,38 +3,54 @@ git-rebase(1) NAME ---- -git-rebase - Rebase local commits to new upstream head +git-rebase - Rebase local commits to a new head SYNOPSIS -------- 'git-rebase' [--onto <newbase>] <upstream> [<branch>] +'git-rebase' --continue + +'git-rebase' --abort + DESCRIPTION ----------- -git-rebase applies to <upstream> (or optionally to <newbase>) commits -from <branch> that do not appear in <upstream>. When <branch> is not -specified it defaults to the current branch (HEAD). +git-rebase replaces <branch> with a new branch of the same name. When +the --onto option is provided the new branch starts out with a HEAD equal +to <newbase>, otherwise it is equal to <upstream>. It then attempts to +create a new commit for each commit from the original <branch> that does +not exist in the <upstream> branch. -When git-rebase is complete, <branch> will be updated to point to the -newly created line of commit objects, so the previous line will not be -accessible unless there are other references to it already. +It is possible that a merge failure will prevent this process from being +completely automatic. You will have to resolve any such merge failure +and run `git rebase --continue`. If you can not resolve the merge +failure, running `git rebase --abort` will restore the original <branch> +and remove the working files found in the .dotest directory. + +Note that if <branch> is not specified on the command line, the currently +checked out branch is used. Assume the following history exists and the current branch is "topic": +------------ A---B---C topic / D---E---F---G master +------------ From this point, the result of either of the following commands: + git-rebase master git-rebase master topic would be: +------------ A'--B'--C' topic / D---E---F---G master +------------ While, starting from the same point, the result of either of the following commands: @@ -44,21 +60,33 @@ commands: would be: +------------ A'--B'--C' topic / D---E---F---G master +------------ In case of conflict, git-rebase will stop at the first problematic commit -and leave conflict markers in the tree. After resolving the conflict manually -and updating the index with the desired resolution, you can continue the -rebasing process with +and leave conflict markers in the tree. You can use git diff to locate +the markers (<<<<<<) and make edits to resolve the conflict. For each +file you edit, you need to tell git that the conflict has been resolved, +typically this would be done with + + + git update-index <filename> + + +After resolving the conflict manually and updating the index with the +desired resolution, you can continue the rebasing process with + + + git rebase --continue - git am --resolved --3way Alternatively, you can undo the git-rebase with - git reset --hard ORIG_HEAD - rm -r .dotest + + git rebase --abort OPTIONS ------- @@ -73,6 +101,28 @@ OPTIONS <branch>:: Working branch; defaults to HEAD. +--continue:: + Restart the rebasing process after having resolved a merge conflict. + +--abort:: + Restore the original branch and abort the rebase operation. + +NOTES +----- +When you rebase a branch, you are changing its history in a way that +will cause problems for anyone who already has a copy of the branch +in their repository and tries to pull updates from you. You should +understand the implications of using 'git rebase' on a repository that +you share. + +When the git rebase command is run, it will first execute a "pre-rebase" +hook if one exists. You can use this hook to do sanity checks and +reject the rebase if it isn't appropriate. Please see the template +pre-rebase hook script for an example. + +You must be in the top directory of your project to start (or continue) +a rebase. Upon completion, <branch> will be the current branch. + Author ------ Written by Junio C Hamano <junkio@cox.net> @@ -135,6 +135,7 @@ extern const char *setup_git_directory(void); extern const char *prefix_path(const char *prefix, int len, const char *path); extern const char *prefix_filename(const char *prefix, int len, const char *path); extern void verify_filename(const char *prefix, const char *name); +extern void verify_non_filename(const char *prefix, const char *name); #define alloc_nr(x) (((x)+16)*3/2) diff --git a/git-fetch.sh b/git-fetch.sh index 83143f82cf..280f62e4b7 100755 --- a/git-fetch.sh +++ b/git-fetch.sh @@ -270,14 +270,22 @@ fetch_main () { if [ -n "$GIT_SSL_NO_VERIFY" ]; then curl_extra_args="-k" fi - remote_name_quoted=$(perl -e ' + max_depth=5 + depth=0 + head="ref: $remote_name" + while (expr "z$head" : "zref:" && expr $depth \< $max_depth) >/dev/null + do + remote_name_quoted=$(perl -e ' my $u = $ARGV[0]; + $u =~ s/^ref:\s*//; $u =~ s{([^-a-zA-Z0-9/.])}{sprintf"%%%02x",ord($1)}eg; print "$u"; - ' "$remote_name") - head=$(curl -nsfL $curl_extra_args "$remote/$remote_name_quoted") && + ' "$head") + head=$(curl -nsfL $curl_extra_args "$remote/$remote_name_quoted") + depth=$( expr \( $depth + 1 \) ) + done expr "z$head" : "z$_x40\$" >/dev/null || - die "Failed to fetch $remote_name from $remote" + die "Failed to fetch $remote_name from $remote" echo >&2 Fetching "$remote_name from $remote" using http git-http-fetch -v -a "$head" "$remote/" || exit ;; diff --git a/git-rebase.sh b/git-rebase.sh index f7b2b9401a..9e259028e0 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -4,37 +4,51 @@ # USAGE='[--onto <newbase>] <upstream> [<branch>]' -LONG_USAGE='git-rebase applies to <upstream> (or optionally to <newbase>) commits -from <branch> that do not appear in <upstream>. When <branch> is not -specified it defaults to the current branch (HEAD). - -When git-rebase is complete, <branch> will be updated to point to the -newly created line of commit objects, so the previous line will not be -accessible unless there are other references to it already. - -Assuming the following history: - - A---B---C topic - / - D---E---F---G master - -The result of the following command: - - git-rebase --onto master~1 master topic - - would be: - - A'\''--B'\''--C'\'' topic - / - D---E---F---G master +LONG_USAGE='git-rebase replaces <branch> with a new branch of the +same name. When the --onto option is provided the new branch starts +out with a HEAD equal to <newbase>, otherwise it is equal to <upstream> +It then attempts to create a new commit for each commit from the original +<branch> that does not exist in the <upstream> branch. + +It is possible that a merge failure will prevent this process from being +completely automatic. You will have to resolve any such merge failure +and run git-rebase --continue. If you can not resolve the merge failure, +running git-rebase --abort will restore the original <branch> and remove +the working files found in the .dotest directory. + +Note that if <branch> is not specified on the command line, the +currently checked out branch is used. You must be in the top +directory of your project to start (or continue) a rebase. + +Example: git-rebase master~1 topic + + A---B---C topic A'\''--B'\''--C'\'' topic + / --> / + D---E---F---G master D---E---F---G master ' - . git-sh-setup unset newbase while case "$#" in 0) break ;; esac do case "$1" in + --continue) + diff=$(git-diff-files) + case "$diff" in + ?*) echo "You must edit all merge conflicts and then" + echo "mark them as resolved using git update-index" + exit 1 + ;; + esac + git am --resolved --3way + exit + ;; + --abort) + [ -d .dotest ] || die "No rebase in progress?" + git reset --hard ORIG_HEAD + rm -r .dotest + exit + ;; --onto) test 2 -le "$#" || usage newbase="$2" diff --git a/pack-objects.c b/pack-objects.c index c0acc460bb..6604338131 100644 --- a/pack-objects.c +++ b/pack-objects.c @@ -1032,12 +1032,6 @@ static int try_delta(struct unpacked *cur, struct unpacked *old, unsigned max_de max_depth -= cur_entry->delta_limit; } - size = cur_entry->size; - oldsize = old_entry->size; - sizediff = oldsize > size ? oldsize - size : size - oldsize; - - if (size < 50) - return -1; if (old_entry->depth >= max_depth) return 0; @@ -1048,9 +1042,12 @@ static int try_delta(struct unpacked *cur, struct unpacked *old, unsigned max_de * more space-efficient (deletes don't have to say _what_ they * delete). */ + size = cur_entry->size; max_size = size / 2 - 20; if (cur_entry->delta) max_size = cur_entry->delta_size-1; + oldsize = old_entry->size; + sizediff = oldsize < size ? size - oldsize : 0; if (sizediff >= max_size) return 0; delta_buf = diff_delta(old->data, oldsize, @@ -1109,6 +1106,9 @@ static void find_deltas(struct object_entry **list, int window, int depth) */ continue; + if (entry->size < 50) + continue; + free(n->data); n->entry = entry; n->data = read_sha1_file(entry->sha1, type, &size); diff --git a/revision.c b/revision.c index d1f85a8f8a..ad78efda51 100644 --- a/revision.c +++ b/revision.c @@ -770,6 +770,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch include = get_reference(revs, next, sha1, flags); if (!exclude || !include) die("Invalid revision range %s..%s", arg, next); + + if (!seen_dashdash) { + *dotdot = '.'; + verify_non_filename(revs->prefix, arg); + } add_pending_object(revs, exclude, this); add_pending_object(revs, include, next); continue; @@ -794,13 +799,20 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch if (seen_dashdash || local_flags) die("bad revision '%s'", arg); - /* If we didn't have a "--", all filenames must exist */ + /* If we didn't have a "--": + * (1) all filenames must exist; + * (2) all rev-args must not be interpretable + * as a valid filename. + * but the latter we have checked in the main loop. + */ for (j = i; j < argc; j++) verify_filename(revs->prefix, argv[j]); revs->prune_data = get_pathspec(revs->prefix, argv + i); break; } + if (!seen_dashdash) + verify_non_filename(revs->prefix, arg); object = get_reference(revs, arg, sha1, flags ^ local_flags); add_pending_object(revs, object, arg); } @@ -80,11 +80,31 @@ void verify_filename(const char *prefix, const char *arg) if (!lstat(name, &st)) return; if (errno == ENOENT) - die("ambiguous argument '%s': unknown revision or filename\n" - "Use '--' to separate filenames from revisions", arg); + die("ambiguous argument '%s': unknown revision or path not in the working tree.\n" + "Use '--' to separate paths from revisions", arg); die("'%s': %s", arg, strerror(errno)); } +/* + * Opposite of the above: the command line did not have -- marker + * and we parsed the arg as a refname. It should not be interpretable + * as a filename. + */ +void verify_non_filename(const char *prefix, const char *arg) +{ + const char *name; + struct stat st; + + if (*arg == '-') + return; /* flag */ + name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg; + if (!lstat(name, &st)) + die("ambiguous argument '%s': both revision and filename\n" + "Use '--' to separate filenames from revisions", arg); + if (errno != ENOENT) + die("'%s': %s", arg, strerror(errno)); +} + const char **get_pathspec(const char *prefix, const char **pathspec) { const char *entry = *pathspec; |