Skip to content

Instantly share code, notes, and snippets.

@OscarL
Last active May 28, 2024 21:18
Show Gist options
  • Save OscarL/a231cc2bf5413299b51fddfd03af4fb0 to your computer and use it in GitHub Desktop.
Save OscarL/a231cc2bf5413299b51fddfd03af4fb0 to your computer and use it in GitHub Desktop.
How to update .patchset files for HaikuPorter recipes, when they do not apply cleanly.

Updating HaikuPorter .recipes (and what to do when applying the .patchset fails).

An example-based, step-by-step guide.

TL;DR

> git am <foobar.patchset>
> git apply --verbose --reject .git/rebase-apply/<FAILED_PATCH_NUMBER>
> <edit files as needed here, look for .rej files to know what need manual intervention>
> git add <file>
> git am --resolved
> git rebase <commit_sha>~                      # Optional
> git commit --amend --reset-author             # Optional
> hp -e <foobar>

Mission statement, and some bumps in the road.

We'll try to update git from version 2.43.2 to version 2.45.1.

We do the usual stuff...

[~ ]
> cd /src/haikuports/dev-vcs/git/
> git pull upstream master
> git switch -c git-2.45.1
> git mv git-2.43.2.recipe git-2.45.1.recipe
> git mv patches/git-2.43.2.patchset patches/git-2.45.1.patchset
> hp -b git

(hp is an alias, in my case: alias hp="time haikuporter -S -j4 --no-source-packages --get-dependencies")

At this point we need to fix the CHECKSUM_SHA256 variables on the recipe, to match the ones of the new version.

After we do that, we try to unpack and apply the patchset again:

> hp -b git
Checking if any dependency-infos need to be updated ...
        updating dependency infos of git-2.45.1
Looking for stale dependency-infos ...
----------------------------------------------------------------------
dev-vcs::git-2.45.1
        /src/haikuports/haikuports/dev-vcs/git/git-2.45.1.recipe
----------------------------------------------------------------------
Skipping download of source for git-2.45.1.tar.xz
Skipping checksum validation of git-2.45.1.tar.xz
Skipping download of source for git-manpages-2.45.1.tar.xz
Skipping checksum validation of git-manpages-2.45.1.tar.xz
Skipping download of source for git-htmldocs-2.45.1.tar.xz
Validating checksum of git-htmldocs-2.45.1.tar.xz
Unpacking source of git-2.45.1.tar.xz
Unpacking source of git-manpages-2.45.1.tar.xz
Unpacking source of git-htmldocs-2.45.1.tar.xz
Initialized empty Git repository in /src/haikuports/haikuports/dev-vcs/git/work-2.45.1/sources/git-2.45.1/.git/
Applying patchset "/src/haikuports/haikuports/dev-vcs/git/patches/git-2.45.1.patchset" ...
error: sha1 information is lacking or useless (builtin/config.c).
error: could not build fake ancestor
hint: Use 'git am --show-current-patch=diff' to see the failed patch
Command '['git', 'am', '--ignore-whitespace', '-3', '--keep-cr', '/src/haikuports/haikuports/dev-vcs/git/patches/git-2.45.1.patchset']' returned non-zero exit status 128.

Ouch! The .patchset file doesn't applies automagically this time, bummer. Let's see how to update it...

Inspecting the damage.

We change dir to the "$sourceDir" to see what the problem was:

[/src/haikuports/haikuports/dev-vcs/git (git-2.45.1)]
> cd work-2.45.1/sources/git-2.45.1/
[/src/haikuports/haikuports/dev-vcs/git/work-2.45.1/sources/git-2.45.1 (haikuport)]
> git status -uno
On branch haikuport
You are in the middle of an am session.
  (fix conflicts and then run "git am --continue")
  (use "git am --skip" to skip this patch)
  (use "git am --abort" to restore the original branch)

nothing to commit (use -u to show untracked files)

We take the hint git gave us earlier, and use the following command to inspect where the (first) problem is:

[/src/haikuports/haikuports/dev-vcs/git/work-2.45.1/sources/git-2.45.1 (haikuport)]
> git am --show-current-patch=diff
diff --git a/builtin/config.c b/builtin/config.c
index 11a4d4e..67dc39d 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -711,24 +711,11 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                char *user_config, *xdg_config;
 
                git_global_config(&user_config, &xdg_config);
-               if (!user_config)
-                       /*
-                        * It is unknown if HOME/.gitconfig exists, so
-                        * we do not know if we should write to XDG
-                        * location; error out even if XDG_CONFIG_HOME
diff --git a/builtin/config.c b/builtin/config.c
index 11a4d4e..67dc39d 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -711,24 +711,11 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                char *user_config, *xdg_config;
 
                git_global_config(&user_config, &xdg_config);
-               if (!user_config)
-                       /*
-                        * It is unknown if HOME/.gitconfig exists, so
-                        * we do not know if we should write to XDG
-                        * location; error out even if XDG_CONFIG_HOME
-                        * is set and points at a sane location.
-                        */
-                       die(_("$HOME not set"));
-
-               given_config_source.scope = CONFIG_SCOPE_GLOBAL;
-
-               if (access_or_warn(user_config, R_OK, 0) &&
-                   xdg_config && !access_or_warn(xdg_config, R_OK, 0)) {
-                       given_config_source.file = xdg_config;
-                       free(user_config);
-               } else {

Abort, abort!

Ok, time to abort, and see where that leaves us.

> git am --abort
> git status .
On branch haikuport
nothing to commit, working tree clean
> git log --oneline
c537e86 (HEAD -> haikuport, tag: ORIGIN) import

We're back to having a clean work dir at least.

Re-play, in slow-motion.

We'll try to apply the patchset manually, letting git do its magic, and resolve problems as they present:

> git am ../../../patches/git-2.45.1.patchset
Applying: git-web--browse.sh: use "open" on Haiku
Applying: On Haiku use the user settings directory instead of HOME
Applying: Ensure config-directory exists before using it.
Applying: Move credential cache to the config directory.
Applying: builtin: config: use xdg_config even if it does not exist
error: patch failed: builtin/config.c:711
error: builtin/config.c: patch does not apply
Patch failed at 0005 builtin: config: use xdg_config even if it does not exist
hint: Use 'git am --show-current-patch=diff' to see the failed patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".

Notice the "Patch failed at 00005" line... That means that from the original .patchset, we got 4 "commits" in, and the 5th failed:

> git log --oneline
caef08d (HEAD -> haikuport) Move credential cache to the config directory.
8707436 Ensure config-directory exists before using it.
af0ee01 On Haiku use the user settings directory instead of HOME
75e283a git-web--browse.sh: use "open" on Haiku
c537e86 (tag: ORIGIN) import

Watching more closely.

At this stage of the git am session, there's a new folder of interest under .git:

> ls .git/rebase-apply/
0001           0005           abort-safety   final-commit   messageid      quiet          threeway       
0002           0006           apply-opt      info           msg            quoted-cr      utf8           
0003           0007           applying       keep           next           scissors       
0004           0008           author-script  last           patch          sign           

Those 000x files are each of the 8 commits contained in our original .patchset, while patch contains the current path that failed to apply.

We will now apply patch 0005, asking git to be more verbose, and to generate .rej files for each "hunk" that fails to apply:

> git apply --verbose --reject .git/rebase-apply/0005
Checking patch builtin/config.c...
error: while searching for:
                char *user_config, *xdg_config;

                git_global_config(&user_config, &xdg_config);
                if (!user_config)
                        /*
                         * It is unknown if HOME/.gitconfig exists, so
                         * we do not know if we should write to XDG
                         * location; error out even if XDG_CONFIG_HOME
                         * is set and points at a sane location.
                         */
                        die(_("$HOME not set"));

                given_config_source.scope = CONFIG_SCOPE_GLOBAL;

                if (access_or_warn(user_config, R_OK, 0) &&
                    xdg_config && !access_or_warn(xdg_config, R_OK, 0)) {
                        given_config_source.file = xdg_config;
                        free(user_config);
                } else {
                        given_config_source.file = user_config;
                        free(xdg_config);
                }
        }
        else if (use_system_config) {

error: patch failed: builtin/config.c:711
Applying patch builtin/config.c with 1 reject...
Rejected hunk #1.

In this case, the patch file only contains one "hunk". If the commit had contained multiple hunks, we could have ended with, say... 2 out of 10 hunks being rejected, while the rest would have been applied automatically, making our job easier.

Time to get ours hands dirty.

Now we need to open "./builtin/config.c" and "./builtin/config.c.rej" in an editor, and do the necessary modifications to that patch.

> lpe ./builtin/config.c ./builtin/config.c.rej

In this particular case, we search for "int cmd_config", and try to figure out what changed.

Tip:
	Go to https://github.com/git/git/compare/v2.43.2..v2.45.1#files_bucket, and search for "config.c",
	or use `> git diff v2.43.2..v2.45.1 config.c`, if you have a full clone of the git repo available.

Seems that some of the logic from ./builtins/config.c's cmd_config() function got moved into ./config.c's git_global_config(), and git_global_config_paths() got added there with the previous logic.

So... we'll move our patch there too.

In this case, we ended with this new diff:

> git diff
diff --git a/config.c b/config.c
index 9625246..3c0b3d7 100644
--- a/config.c
+++ b/config.c
@@ -2013,18 +2013,12 @@ char *git_global_config(void)
        char *user_config, *xdg_config;
 
        git_global_config_paths(&user_config, &xdg_config);
-       if (!user_config) {
-               free(xdg_config);
-               return NULL;
-       }
-
-       if (access_or_warn(user_config, R_OK, 0) && xdg_config &&
-           !access_or_warn(xdg_config, R_OK, 0)) {
-               free(user_config);
-               return xdg_config;
-       } else {
-               free(xdg_config);
+       if (!xdg_config)
                return user_config;
+       else {
+               if (user_config)
+                       free(user_config);
+               return xdg_config;
        }
 }
>
> git status
On branch haikuport
You are in the middle of an am session.
  (fix conflicts and then run "git am --continue")
  (use "git am --skip" to skip this patch)
  (use "git am --abort" to restore the original branch)

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   config.c

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        builtin/config.c.rej

no changes added to commit (use "git add" and/or "git commit -a")

Now we remove the .rej file(s), and add the modified file(s) to the index:

> rm builtin/config.c.rej
> git add config.c

If 0005 had contained more hunks that applied cleanly, we would have needed to git add those at this point.

Now, let git know that we have resolved the problem with patch 0005, so it can continue applying the remaining ones:

> git am --resolved
Applying: builtin: config: use xdg_config even if it does not exist
Applying: Fix detection of Haiku for git web browse
Applying: ignore test failures.
error: patch failed: t/Makefile:63
error: t/Makefile: patch does not apply
Patch failed at 0007 ignore test failures.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".

Oops! Another patch doesn't apply. We repeat the process:

> git apply --verbose --reject .git/rebase-apply/0007
Checking patch t/Makefile...
error: while searching for:
        $(MAKE) clean-except-prove-cache

$(T):
        @echo "*** $@ ***"; '$(TEST_SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)

pre-clean:
        $(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)'

error: patch failed: t/Makefile:63
Applying patch t/Makefile with 1 reject...
Rejected hunk #1.
>
> lpe t/Makefile t/Makefile.rej 
>
> git diff
diff --git a/t/Makefile b/t/Makefile
index 2d95046..4cc0249 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -71,7 +71,7 @@ prove: pre-clean check-chainlint $(TEST_LINT)
        $(MAKE) clean-except-prove-cache
 
 $(T):
-       @echo "*** $@ ***"; '$(TEST_SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
+       - @echo "*** $@ ***"; '$(TEST_SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
 
 $(UNIT_TESTS):
        @echo "*** $@ ***"; $@
>
> git status
On branch haikuport
You are in the middle of an am session.
  (fix conflicts and then run "git am --continue")
  (use "git am --skip" to skip this patch)
  (use "git am --abort" to restore the original branch)

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   t/Makefile

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        t/Makefile.rej

no changes added to commit (use "git add" and/or "git commit -a")
>
> git add t/Makefile
>
> rm t/Makefile.rej
>
> git am --resolved
Applying: ignore test failures.
Applying: git-gui: Use symbolic links.
> 

Hurray! We got all patches in:

> git log --oneline
ca45b2a (HEAD -> haikuport) git-gui: Use symbolic links.
f2733c3 ignore test failures.
62c6454 Fix detection of Haiku for git web browse
cdc336f builtin: config: use xdg_config even if it does not exist
caef08d Move credential cache to the config directory.
8707436 Ensure config-directory exists before using it.
af0ee01 On Haiku use the user settings directory instead of HOME
75e283a git-web--browse.sh: use "open" on Haiku
c537e86 (tag: ORIGIN) import

Updating commit messages.

Now we need to fix up the commit message on commit "cdc336f" above, as its message doesn't reflects the current change.

We'll use an interactive rebase here, from that commit, to the newest one:

> git rebase -i cdc336f~

Notice the ~ at the end of the partial commit id. That means "from this commit onwards".

On the editor that popups, we change "pick" to "edit" for that commit, save and quit the editor, and we get the following output:

Stopped at cdc336f...  builtin: config: use xdg_config even if it does not exist
You can amend the commit now, with

  git commit --amend 

Once you are satisfied with your changes, run

  git rebase --continue
> 

We will just change the author and commit message, and continue the rebase:

[/src/haikuports/haikuports/dev-vcs/git/work-2.45.1/sources/git-2.45.1 ((no branch, rebasing haikuport))]
> git commit --amend --reset-author
[detached HEAD 9f4d2c8] config: use "xdg_config" even if "user_config" does not exist
 1 file changed, 5 insertions(+), 11 deletions(-)
>
> git rebase --continue
Successfully rebased and updated refs/heads/haikuport.
[/src/haikuports/haikuports/dev-vcs/git/work-2.45.1/sources/git-2.45.1 (haikuport)]
> 

We inspect the commits, just in case:

> git log --oneline
a0f34cd (HEAD -> haikuport) git-gui: Use symbolic links.
0a0191c ignore test failures.
2fcd662 Fix detection of Haiku for git web browse
9f4d2c8 config: use "xdg_config" even if "user_config" does not exist
caef08d Move credential cache to the config directory.
8707436 Ensure config-directory exists before using it.
af0ee01 On Haiku use the user settings directory instead of HOME
75e283a git-web--browse.sh: use "open" on Haiku
c537e86 (tag: ORIGIN) import
> 

All seems well. Now we can finally ask HaikuPorter to extract a new .patchset for us!

> hp -e git
Checking if any dependency-infos need to be updated ...
Looking for stale dependency-infos ...
Extracting patchset for git-2.45.1.tar.xz to /src/haikuports/haikuports/dev-vcs/git/patches/git-2.45.1.patchset
Extracting patchset for git-manpages-2.45.1.tar.xz to /src/haikuports/haikuports/dev-vcs/git/patches/git-2.45.1-source2.patchset
fatal: ambiguous argument 'ORIGIN': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'
Command '['git', '-c', 'core.abbrev=auto', 'format-patch', '-kp', '--stdout', 'ORIGIN']' returned non-zero exit status 128.
>

The error here is because this recipe has several SOURCE_URIs, and at this stage, HaikuPorter hasn't initialized their .git repos yet (as we're still working on applying the changes for the first SOURCE_URI).

We now just do some clean up, and continue with the recipe/package update for the git package:

[/src/haikuports/haikuports/dev-vcs/git/work-2.45.1/sources/git-2.45.1 (haikuport)]
> cd ../../../
[/src/haikuports/haikuports/dev-vcs/git (git-2.45.1)]
> rm patches/git-2.45.1-source2.patchset

That patches/git-2.45.1-source2.patchset is a left over from the previous haikuport -e git error.

Now we try to unpack and patch sources one last time by invoking > hp -b git.

We should have no errors at this point, and current state should be:

[/src/haikuports/haikuports/dev-vcs/git (git-2.45.1)]
> git status .
On branch git-2.45.1
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        renamed:    git-2.43.2.recipe -> git-2.45.1.recipe
        renamed:    patches/git-2.43.2.patchset -> patches/git-2.45.1.patchset

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   git-2.45.1.recipe
        modified:   patches/git-2.45.1.patchset

> 

Time to wrap-up!

We commit our changes...

> git add git-2.45.1.recipe patches/git-2.45.1.patchset
> git commit -m "git: update to version 2.45.1"

And now we build it... and ship it!

> hp git
> git push origin git-2.45.1

Bonus track.

In case you find it useful, this is my PS1 prompt, that shows [current_dir (current_git_branch_if_any)], and > on the next line:

function _fancy_prompt {
	local __prev_rc_and_loc='\[`if [ $? = 0 ]; then echo "\e[32m"; else echo "\e[31m"; fi`\]\w\[\e[0m\]'
	local __git_branch_color="\[\033[31m\]"
	local __git_branch='`git branch 2> /dev/null | grep -e ^*| sed -E s/^\\\\\*\ \(.+\)$/\(\\\\\1\)\/`'
	local __last_color="\[\033[00m\]"
	export PS1="[$__prev_rc_and_loc $__git_branch_color$__git_branch$__last_color]\n> "
}

_fancy_prompt

Just add the above to your bash profile/bashrc.

@OscarL
Copy link
Author

OscarL commented May 28, 2024

@korli: good point, should make it easier to remember/execute.

I just wasn't sure if using just the patch file would retain the commit metadata (assuming one intends to preserve that), and as the individual 000n files did had author/date/subject... I just went with that.

I'll certainly try with just git apply --verbose --reject ./git/rebase-apply/patch the next time.

Thanks for the feedback!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment