diff --git a/Documentation/RelNotes/2.52.0.adoc b/Documentation/RelNotes/2.52.0.adoc index 9f8607a75240ad..3625db515ef963 100644 --- a/Documentation/RelNotes/2.52.0.adoc +++ b/Documentation/RelNotes/2.52.0.adoc @@ -86,6 +86,30 @@ including security updates, are included in this release. ignored") did not work well with "--name-only" and friends. (merge b55e6d36eb ly/diff-name-only-with-diff-from-content later to maint). + * Documentation for "git rebase" has been updated. + (merge 3f7f2b0359 je/doc-rebase later to maint). + + * The start_delayed_progress() function in the progress eye-candy API + did not clear its internal state, making an initial delay value + larger than 1 second ineffective, which has been corrected. + (merge 457534d041 js/progress-delay-fix later to maint). + + * The compatObjectFormat extension is used to hide an incomplete + feature that is not yet usable for any purpose other than + developing the feature further. Document it as such to discourage + its use by mere mortals. + (merge 716d905792 bc/doc-compat-object-format-not-working later to maint). + + * "git log -L..." compared trees of multiple parents with the tree of the + merge result in an unnecessarily inefficient way. + (merge 0a15bb634c sg/line-log-merge-optim later to maint). + + * Under a race against another process that is repacking the + repository, especially a partially cloned one, "git fetch" may + mistakenly think some objects we do have are missing, which has + been corrected. + (merge 8f32a5a6c0 jk/fetch-check-graph-objects-fix later to maint). + * Other code cleanup, docfix, build fix, etc. (merge 823d537fa7 kh/doc-git-log-markup-fix later to maint). (merge cf7efa4f33 rj/t6137-cygwin-fix later to maint). diff --git a/Documentation/config/extensions.adoc b/Documentation/config/extensions.adoc index 9e2f321a6d776f..829f2523fcbca4 100644 --- a/Documentation/config/extensions.adoc +++ b/Documentation/config/extensions.adoc @@ -14,6 +14,10 @@ compatObjectFormat:: compatObjectFormat. As well as being able to use oids encoded in compatObjectFormat in addition to oids encoded with objectFormat to locally specify objects. ++ +Note that the functionality enabled by this extension is incomplete and subject +to change. It currently exists only to allow development and testing of +the underlying feature and is not designed to be enabled by end users. noop:: This extension does not change git's behavior at all. It is useful only diff --git a/Documentation/git-rebase.adoc b/Documentation/git-rebase.adoc index 727160c6db77fc..005caf61646ec7 100644 --- a/Documentation/git-rebase.adoc +++ b/Documentation/git-rebase.adoc @@ -16,49 +16,12 @@ SYNOPSIS DESCRIPTION ----------- -If `` is specified, `git rebase` will perform an automatic -`git switch ` before doing anything else. Otherwise -it remains on the current branch. +Transplant a series of commits onto a different starting point. +You can also use `git rebase` to reorder or combine commits: see INTERACTIVE +MODE below for how to do that. -If `` is not specified, the upstream configured in -`branch..remote` and `branch..merge` options will be used (see -linkgit:git-config[1] for details) and the `--fork-point` option is -assumed. If you are currently not on any branch or if the current -branch does not have a configured upstream, the rebase will abort. - -All changes made by commits in the current branch but that are not -in `` are saved to a temporary area. This is the same set -of commits that would be shown by `git log ..HEAD`; or by -`git log 'fork_point'..HEAD`, if `--fork-point` is active (see the -description on `--fork-point` below); or by `git log HEAD`, if the -`--root` option is specified. - -The current branch is reset to `` or `` if the -`--onto` option was supplied. This has the exact same effect as -`git reset --hard ` (or ``). `ORIG_HEAD` is set -to point at the tip of the branch before the reset. - -[NOTE] -`ORIG_HEAD` is not guaranteed to still point to the previous branch tip -at the end of the rebase if other commands that write that pseudo-ref -(e.g. `git reset`) are used during the rebase. The previous branch tip, -however, is accessible using the reflog of the current branch -(i.e. `@{1}`, see linkgit:gitrevisions[7]). - -The commits that were previously saved into the temporary area are -then reapplied to the current branch, one by one, in order. Note that -any commits in `HEAD` which introduce the same textual changes as a commit -in `HEAD..` are omitted (i.e., a patch already accepted upstream -with a different commit message or timestamp will be skipped). - -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`. Another option is to bypass the commit -that caused the merge failure with `git rebase --skip`. To check out the -original `` and remove the `.git/rebase-apply` working files, use -the command `git rebase --abort` instead. - -Assume the following history exists and the current branch is "topic": +For example, imagine that you have been working on the `topic` branch in this +history, and you want to "catch up" to the work done on the `master` branch. ------------ A---B---C topic @@ -66,13 +29,11 @@ Assume the following history exists and the current branch is "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: +You want to transplant the commits you made on `topic` since it diverged from +`master` (i.e. A, B, and C), on top of the current `master`. You can do this +by running `git rebase master` while the `topic` branch is checked out. If you +want to rebase `topic` while on another branch, `git rebase master topic` is a +shortcut for `git checkout topic && git rebase master`. ------------ A'--B'--C' topic @@ -80,30 +41,56 @@ would be: D---E---F---G master ------------ -*NOTE:* The latter form is just a short-hand of `git checkout topic` -followed by `git rebase master`. When rebase exits `topic` will -remain the checked-out branch. -If the upstream branch already contains a change you have made (e.g., -because you mailed a patch which was applied upstream), then that commit -will be skipped and warnings will be issued (if the 'merge' backend is -used). For example, running `git rebase master` on the following -history (in which `A'` and `A` introduce the same set of changes, but -have different committer information): +If there is a merge conflict during this process, `git rebase` will stop at the +first problematic commit and leave conflict markers. If this happens, you can do +one of these things: ------------- - A---B---C topic - / - D---E---A'---F master ------------- +1. Resolve the conflict. You can use `git diff` to find 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. You can mark the conflict as + resolved with `git add `. After resolving all of the conflicts, + you can continue the rebasing process with -will result in: + git rebase --continue ------------- - B'---C' topic - / - D---E---A'---F master ------------- +2. Stop the `git rebase` and return your branch to its original state with + + git rebase --abort + +3. Skip the commit that caused the merge conflict with + + git rebase --skip + +If you don't specify an `` to rebase onto, the upstream configured in +`branch..remote` and `branch..merge` options will be used (see +linkgit:git-config[1] for details) and the `--fork-point` option is +assumed. If you are currently not on any branch or if the current +branch does not have a configured upstream, the rebase will abort. + +Here is a simplified description of what `git rebase ` does: + +1. Make a list of all commits on your current branch since it branched + off from `` that do not have an equivalent commit in + ``. +2. Check out `` with the equivalent of + `git checkout --detach `. +3. Replay the commits, one by one, in order. This is similar to running + `git cherry-pick ` for each commit. See REBASING MERGES for how merges + are handled. +4. Update your branch to point to the final commit with the equivalent + of `git checkout -B `. + +[NOTE] +When starting the rebase, `ORIG_HEAD` is set to point to the commit at the tip +of the to-be-rebased branch. However, `ORIG_HEAD` is not guaranteed to still +point to that commit at the end of the rebase if other commands that change +`ORIG_HEAD` (like `git reset`) are used during the rebase. The previous branch +tip, however, is accessible using the reflog of the current branch (i.e. `@{1}`, +see linkgit:gitrevisions[7]. + +TRANSPLANTING A TOPIC BRANCH WITH --ONTO +---------------------------------------- Here is how you would transplant a topic branch based on one branch to another, to pretend that you forked the topic branch @@ -186,28 +173,6 @@ This is useful if F and G were flawed in some way, or should not be part of topicA. Note that the argument to `--onto` and the `` parameter can be any valid commit-ish. -In case of conflict, `git rebase` will stop at the first problematic commit -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 add - - -After resolving the conflict manually and updating the index with the -desired resolution, you can continue the rebasing process with - - - git rebase --continue - - -Alternatively, you can undo the 'git rebase' with - - - git rebase --abort - MODE OPTIONS ------------ @@ -253,6 +218,8 @@ As a special case, you may use "A\...B" as a shortcut for the merge base of A and B if there is exactly one merge base. You can leave out at most one of A and B, in which case it defaults to HEAD. +See TRANSPLANTING A TOPIC BRANCH WITH --ONTO above for examples. + --keep-base:: Set the starting point at which to create the new commits to the merge base of `` and ``. Running diff --git a/Documentation/git.adoc b/Documentation/git.adoc index 743b7b00e4d751..03e9e69d257c1d 100644 --- a/Documentation/git.adoc +++ b/Documentation/git.adoc @@ -684,7 +684,7 @@ other `GIT_PROGRESS_DELAY`:: A number controlling how many seconds to delay before showing - optional progress indicators. Defaults to 2. + optional progress indicators. Defaults to 1. `GIT_EDITOR`:: This environment variable overrides `$EDITOR` and `$VISUAL`. diff --git a/fetch-pack.c b/fetch-pack.c index 20e5533b218542..6ed566295189dc 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -143,7 +143,8 @@ static struct commit *deref_without_lazy_fetch(const struct object_id *oid, commit = lookup_commit_in_graph(the_repository, oid); if (commit) { if (mark_tags_complete_and_check_obj_db) { - if (!odb_has_object(the_repository->objects, oid, 0)) + if (!odb_has_object(the_repository->objects, oid, + HAS_OBJECT_RECHECK_PACKED)) die_in_commit_graph_only(oid); } return commit; diff --git a/line-log.c b/line-log.c index 07f2154e84b843..188d387d40f58d 100644 --- a/line-log.c +++ b/line-log.c @@ -1087,13 +1087,6 @@ static struct diff_filepair *diff_filepair_dup(struct diff_filepair *pair) return new_filepair; } -static void free_diffqueues(int n, struct diff_queue_struct *dq) -{ - for (int i = 0; i < n; i++) - diff_queue_clear(&dq[i]); - free(dq); -} - static int process_all_files(struct line_log_data **range_out, struct rev_info *rev, struct diff_queue_struct *queue, @@ -1189,7 +1182,7 @@ static int process_ranges_ordinary_commit(struct rev_info *rev, struct commit *c struct line_log_data *range) { struct commit *parent = NULL; - struct diff_queue_struct queue; + struct diff_queue_struct queue = DIFF_QUEUE_INIT; struct line_log_data *parent_range; int changed; @@ -1209,9 +1202,7 @@ static int process_ranges_ordinary_commit(struct rev_info *rev, struct commit *c static int process_ranges_merge_commit(struct rev_info *rev, struct commit *commit, struct line_log_data *range) { - struct diff_queue_struct *diffqueues; struct line_log_data **cand; - struct commit **parents; struct commit_list *p; int i; int nparents = commit_list_count(commit->parents); @@ -1220,28 +1211,27 @@ static int process_ranges_merge_commit(struct rev_info *rev, struct commit *comm if (nparents > 1 && rev->first_parent_only) nparents = 1; - ALLOC_ARRAY(diffqueues, nparents); CALLOC_ARRAY(cand, nparents); - ALLOC_ARRAY(parents, nparents); - - p = commit->parents; - for (i = 0; i < nparents; i++) { - parents[i] = p->item; - p = p->next; - queue_diffs(range, &rev->diffopt, &diffqueues[i], commit, parents[i]); - } - for (i = 0; i < nparents; i++) { + for (p = commit->parents, i = 0; + p && i < nparents; + p = p->next, i++) { + struct commit *parent = p->item; + struct diff_queue_struct diffqueue = DIFF_QUEUE_INIT; int changed; - changed = process_all_files(&cand[i], rev, &diffqueues[i], range); + + queue_diffs(range, &rev->diffopt, &diffqueue, commit, parent); + + changed = process_all_files(&cand[i], rev, &diffqueue, range); + diff_queue_clear(&diffqueue); if (!changed) { /* * This parent can take all the blame, so we * don't follow any other path in history */ - add_line_range(rev, parents[i], cand[i]); + add_line_range(rev, parent, cand[i]); free_commit_list(commit->parents); - commit_list_append(parents[i], &commit->parents); + commit_list_append(parent, &commit->parents); ret = 0; goto out; @@ -1252,14 +1242,15 @@ static int process_ranges_merge_commit(struct rev_info *rev, struct commit *comm * No single parent took the blame. We add the candidates * from the above loop to the parents. */ - for (i = 0; i < nparents; i++) - add_line_range(rev, parents[i], cand[i]); + for (p = commit->parents, i = 0; + p && i < nparents; + p = p->next, i++) + add_line_range(rev, p->item, cand[i]); ret = 1; out: clear_commit_line_range(rev, commit); - free(parents); for (i = 0; i < nparents; i++) { if (!cand[i]) continue; @@ -1267,7 +1258,6 @@ static int process_ranges_merge_commit(struct rev_info *rev, struct commit *comm free(cand[i]); } free(cand); - free_diffqueues(nparents, diffqueues); return ret; /* NEEDSWORK evil merge detection stuff */ @@ -1283,10 +1273,10 @@ int line_log_process_ranges_arbitrary_commit(struct rev_info *rev, struct commit struct line_log_data *prange = line_log_data_copy(range); add_line_range(rev, commit->parents->item, prange); clear_commit_line_range(rev, commit); - } else if (!commit->parents || !commit->parents->next) - changed = process_ranges_ordinary_commit(rev, commit, range); - else + } else if (commit->parents && commit->parents->next) changed = process_ranges_merge_commit(rev, commit, range); + else + changed = process_ranges_ordinary_commit(rev, commit, range); } if (!changed) diff --git a/progress.c b/progress.c index 8d5ae70f3a9ec7..8315bdc3d4a77a 100644 --- a/progress.c +++ b/progress.c @@ -114,16 +114,19 @@ static void display(struct progress *progress, uint64_t n, const char *done) const char *tp; struct strbuf *counters_sb = &progress->counters_sb; int show_update = 0; + int update = !!progress_update; int last_count_len = counters_sb->len; - if (progress->delay && (!progress_update || --progress->delay)) + progress_update = 0; + + if (progress->delay && (!update || --progress->delay)) return; progress->last_value = n; tp = (progress->throughput) ? progress->throughput->display.buf : ""; if (progress->total) { unsigned percent = n * 100 / progress->total; - if (percent != progress->last_percent || progress_update) { + if (percent != progress->last_percent || update) { progress->last_percent = percent; strbuf_reset(counters_sb); @@ -133,7 +136,7 @@ static void display(struct progress *progress, uint64_t n, const char *done) tp); show_update = 1; } - } else if (progress_update) { + } else if (update) { strbuf_reset(counters_sb); strbuf_addf(counters_sb, "%"PRIuMAX"%s", (uintmax_t)n, tp); show_update = 1; @@ -166,7 +169,6 @@ static void display(struct progress *progress, uint64_t n, const char *done) } fflush(stderr); } - progress_update = 0; } } @@ -281,7 +283,7 @@ static int get_default_delay(void) static int delay_in_secs = -1; if (delay_in_secs < 0) - delay_in_secs = git_env_ulong("GIT_PROGRESS_DELAY", 2); + delay_in_secs = git_env_ulong("GIT_PROGRESS_DELAY", 1); return delay_in_secs; }