From f11bc5f603712f54ba044c365a66aed538a546d5 Mon Sep 17 00:00:00 2001 From: Aaron Rainbolt Date: Sun, 28 Dec 2025 23:11:59 -0600 Subject: [PATCH] Add Git introduction --- lubuntu-packaging-guide.rst | 834 +++++++++++++++++++++++++++++++++++- 1 file changed, 831 insertions(+), 3 deletions(-) diff --git a/lubuntu-packaging-guide.rst b/lubuntu-packaging-guide.rst index 25affa8..37ea835 100644 --- a/lubuntu-packaging-guide.rst +++ b/lubuntu-packaging-guide.rst @@ -35,7 +35,7 @@ files that we'll describe as we go. To get an idea of how source packages are structured, we'll start with a fairly simple package, ``lxqt-about``:: - aaron@pkg-builder:~/lubuntu-dev/lxqt-about$ tree + aaron@pkg-builder:~/vmshare/pkg/lxqt-about$ tree . ├── aboutdialog │   ... @@ -707,5 +707,833 @@ from there and build it. ``lxqt-about`` deb file, alongside several other files. That's it! If the above worked, you now have a working packaging environment! -We're now ready to move past system setup, and start learning how packaging -works. +We're now ready to move past system setup, and start learning how to do Debian +packaging. + +Git essentials +-------------- + +.. NOTE:: + If you already have a decent working knowledge of Git, you can skim this + section and only focus on the parts that have to do with Debian packages. + +If you're working with Debian packages, you are almost certainly going to have +to work with Git. Git is, in essence, a tool for keeping track of changes that +are made to files in a directory. As changes are made to the directory, +developers use Git to take snapshots of what they changed and how. This is +very useful in both software development and packaging, because: + +* Keeping track of changes in this way allows you to look at how things + changed in the past if needed. +* If something important is lost in the course of changes, you can get it + back out of Git's history. +* If some changes break everything, you can revert them and go back to a + previous commit. +* Each time someone changes something, their identity is attached to that + change, meaning you can determine who changed what, and when. +* If multiple people are changing the same file at the same time, Git can keep + track of those changes independently, then merge them together later so that + nothing is lost. + +Because of the large number of uses Git has, it can be a tricky tool to get +used to. It has far too many features to fully explain in this guide, so we're +only going to go over the basics here. If you want to learn more about Git +than this guide covers, you should read the free +`Pro Git e-book `__. Git also has a number of +manual pages you can browse through, see ``man git``. + +Repos, commits, branches, tags, and remotes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The primary purpose of Git is manipulating Git *repositories*. A repository +(or "repo" for short) is nothing more than a directory full of files, with a +hidden ``.git`` subdirectory in it. The files in this directory are just +regular files, like you're already used to working with. All of Git's state +(the repo history and whatnot) is stored in the ``.git`` directory. Git is +intended to be used from the command line, and provides a number of commands +for repo manipulation. These commands usually affect whatever repo you are +"in". For instance, if ``$HOME/my-project`` is a Git repo, you are "in" the +``my-project`` repo if your current working directory is ``$HOME/my-project`` +or any subdirectory underneath it. + +As you make changes to a Git repository, you will want to *commit* your +changes from time to time. A Git commit is a bit of data that records what +files changed, what lines in those files changed, how those lines changed, and +who changed them. Each commit has a unique ID bound to it, so you can +unambiguously refer to any commit in the repository. When you create a new +commit, that's called "committing" a change. Commits can be *checked out*, +meaning that the files in the repository will be changed to reflect what the +repo looked like at the time that commit was made. + +.. NOTE:: + Git commits can track both large and small changes, but generally it's most + useful if you try to make each Git commit correspond to one logical change + in a project. For instance, if you need to change the version number of a + package, fix an issue that prevents the package from building, make the + README clearer, and introduce a patch to change default settings, each of + those changes should go into separate commits. On the other hand, if you + need to fix a common typo that occur in ten files, it's fine to put all of + those fixes in one commit. + +Being able to keep track of changes as they're made is useful, but it isn't +enough for most projects. You might have multiple people making changes to a +project at the same time. You might want to make some experimental changes +without having to do a bunch of messy reverts if they don't work out. You +might even need to maintain different versions of the same project as if they +were separate sub-projects. To handle these scenarios, Git allows you to +create *branches* within a repo. Each branch is essentially a separate +timeline, tracking one or more commits entirely separate from other commits. +If one commit is made to one branch and another commit is made to another +branch, each branch will only "see" the commit that was made on them. If you +want to take all of the commits from one branch and integrate them into +another, you can *merge* branches. Each branch has a *tip*, which is the +commit most recently made to that branch. Similar to commits, branches can be +checked out. Whatever commit is on the tip of the checked-out branch will end +up checked out when you do this. (Note that checking out a branch is *not* +exactly the same as checking out the commit at the tip of a branch, we'll +cover the differences further later.) + +Every so often, you'll create a commit that is, for one reason or another, +special. You may want to quickly look at this commit later, without having to +analyze the repo to figure out where it's at. Git provides *tags* for marking +these special commits. One common use of tags is to mark the last commit made +before releasing a new version of a project. The tagged commit can then be +used to look at the state the repo was in when that version of the project was +released. Tags can be checked out too; checking out a tag is exactly identical +to checking out the commit a tag is associated with. + +All of the above features work locally, on your system. Again, a Git repo is +nothing more than a directory with a hidden ``.git`` subdirectory. So what +happens when you want to publish your changes for others to see? How do you +receive changes from others? This is where Git remotes come into play. A Git +remote is a server that has a copy of a Git repo on it. As you make changes to +your copy of the repo, you can *push* a branch to the remote; this will send +commits that you've created to the remote, which will then integrate those +commits into a branch of its copy of the repo. If other people have made +changes to a branch on the remote, and you want to download them, you can +*pull* that branch. Last, but not least, if you want a copy of a Git repo you +don't have yet, you can *clone* the repo from the remote. + +Armed with an understanding of these concepts, you are now ready to work with +Git. + +Analyzing a Git repo +^^^^^^^^^^^^^^^^^^^^ + +Remember running ``git clone`` to download the ``lxqt-about-packaging`` +project earlier? (See "Testing the packaging environment" above if you don't +remember this.) The ``git clone`` command *clones* a repository. That means we +have a fully functional Git repo already downloaded, so let's take a closer +look at it and see what's in there! + +Ensure the ``lubuntu-dev`` window is open and the VM is running. Then open +QTerminal, and change to the ``lxqt-about-packaging`` directory:: + + cd $HOME/vmshare/pkg/lxqt-about-packaging + +Now that we're here, let's look at some basic info about the repo, and see if +any changes have been made to it that haven't been comitted yet. To do this, +run ``git status``. Assuming you haven't changed the repo since we ran the +``sbuild`` command previously, you should see something like this:: + + On branch ubuntu/resolute + Your branch is up to date with 'origin/ubuntu/resolute'. + + nothing to commit, working tree clean + +Let's break down what this means: + +* One of the branches in this repo is called ``ubuntu/resolute``. This is the + branch we are actively "on", meaning that all the files in the project's + directory reflect Git's idea of the files on the ``ubuntu/resolute`` branch. + If we make any changes to these files and commit them, our new commit will + end up added to the ``ubuntu/resolute`` branch. +* The ``ubuntu/resolute`` branch does not just exist on our machine, it also + exists on the Git remote named ``origin``. To Git's awareness, our copy of + ``ubuntu/resolute`` matches ``origin``'s copy of ``ubuntu/resolute``. +* All of the files in the repo match exactly what's been committed to the + ``ubuntu/resolute`` branch (well, all of the files Git cares about anyway, + we'll look at that closer in a bit). We don't have any changes that need + committed. (What we've been calling the "files in the repo", Git calls the + "working tree".) + +Most of this is straightforward, except... what is this remote named +``origin``? Let's see what remotes we have:: + + git remote + +Running this command should print the following:: + + origin + +Well that's not particularly useful. Let's look at what the ``origin`` remote +actually points to:: + + git remote get-url origin + +This will output the following:: + + https://git.lubuntu.me/Lubuntu/lxqt-about-packaging.git + +That's much more useful. The ``origin`` remote points to the +``lxqt-about-packaging`` repo on Lubuntu's Gitea instance. + +.. NOTE:: + When you clone a repository, Git automatically takes the URL you cloned the + repo from, and turns it into the ``origin`` remote. You can have more than + one remote in a repository, and if you spend enough time packaging you'll + probably run into situations where being able to do this is useful, but for + now we'll focus on managing a repo with just one remote. + +Git repos generally are worked on by multiple people. Lubuntu's packaging +repos are no exception; each packager needs to be able to upload their changes +to a packaging repo after preparing the packaging for a Lubuntu component. +For this reason, it's generally a good idea to download the latest changes to +a repo before beginning work on it. Let's do that now, just in case someone's +modified ``lxqt-about-packaging``:: + + git pull + +If there were changes, you'll see some info about the download, that starts +with something like this:: + + remote: Enumerating objects: 54, done. + remote: Counting objects: 100% (54/54), done. + remote: Compressing objects: 100% (29/29), done. + remote: Total 54 (delta 26), reused 51 (delta 23), pack-reused 0 (from 0) + Unpacking objects: 100% (54/54), 331.53 KiB | 1.03 MiB/s, done. + From git.lubuntu.me:Lubuntu/lxqt-about-packaging + Updating 98c3794..fe38a66 + Fast-forward + debian/changelog | 1 + + debian/control | 2 +- + debian/copyright | 2 +- + debian/watch | 11 ++++++----- + 4 files changed, 9 insertions(+), 7 deletions(-) + +Exactly what Git shows here may vary, since it's showing a summary of the +download process, the changes that were downloaded, whether they apply to the +active branch or not, etc. For now we don't have to worry about most of this, +we'll cover how to deal with the edge cases later. + +Alternatively, if there weren't any changes, you'll see:: + + Already up to date. + +Now that we have our repo up-to-date, let's look at some of the most recent +commits and see what people have been doing lately:: + + git log + +This will output something like this:: + + commit fe38a662e5c7e0c87d31a25097e9dc29dfe23554 (HEAD -> ubuntu/resolute, origin/ubuntu/resolute) + Author: Aaron Rainbolt + Date: Tue Nov 18 13:16:56 2025 -0600 + + Use debian/watch version 5 (taken from Debian's packages) + + commit f90b89bffb8c04c1a0a9979cc02bd077a1d2deff (tag: ubuntu/2.2.0-0ubuntu1, origin/ubuntu/questing) + Author: Aaron Rainbolt + Date: Thu Jul 31 16:16:32 2025 -0500 + + Update build deps + + commit 98c37947c4abdcda7487893cf15a3b73a26d3572 + Author: Aaron Rainbolt + Date: Thu Jul 31 16:16:00 2025 -0500 + + Bump Standards-Version + + commit 65c030aacf544aa46ca0c9159853e3da05edc3e2 + Author: Aaron Rainbolt + Date: Thu Jul 31 16:15:30 2025 -0500 + + Update copyright file + + commit 9f9ab07b6c8483ad890dda9b16f9883f6815a49d + Author: Aaron Rainbolt + Date: Thu Jul 31 16:14:31 2025 -0500 + + Bump version for new upstream release + +You'll also notice your Bash prompt is gone, and instead your cursor is +sitting directly after a ``:`` character. That's because you're in a *pager*, +allowing you to scroll the Git log. You can scroll in any direction (including +left and right) using the arrow keys, and can scroll up and down quickly using +the Page Up and Page Down keys. To exit the pager and get back to a normal +Bash prompt, press ``q``. + +Now that we're out of the pager, let's look closer at what all this info +means. You don't have to fully understand all of this yet, you can come back +and reference this later. + +* Each "section" (separated from adjacent sections by newlines) corresponds to + a single commit. +* Each commit has a unique ID (this is that long hexadecimal number you see + after ``commit`` in each commit entry). +* Each commit has an author, the person who actually wrote the changes that + went into the commit. Authors are identified by name and email address. + (This is why we configured your name and email into Git when we set up the + packaging environment; this info will end up attached to commits you + create.) +* Each commit has a date. This is the exact date and time at which the commit + was created. +* Each commit has a *commit message*. Generally when you make changes to a + repository, you want to document *what* you changed, and *why*. These + records should be put into commit messages. Many repositories (especially + Lubuntu's packaging repositories) just have one-line commit messages briefly + summarizing what was done. +* Some commits have one or more branches displayed next to them. These commits + are on the tip of each respective branch. For instance, if we were to check + out ``ubuntu/resolute``, we'd end up on commit + ``fe38a662e5c7e0c87d31a25097e9dc29dfe23554``. + +.. NOTE:: + Commit IDs are ridiculously long, hard to type, and just about impossible + to remember. To work around this, you can use the first several characters + of a commit ID in place of a full commit ID in most instances. It's + traditional to use the first seven or eight characters, since it's rare for + two commits in the same repo to ever have the same first seven characters, + and it's still short enough to be temporarily memorized and typed without + too much trouble. + +* Some commits also have *remote* branches displayed next to them. Remember + when we saw ``origin/ubuntu/resolute`` earlier? That was referring to + ``origin``'s copy of ``ubuntu/resolute``. Well, we see it again here. We + also see ``origin/ubuntu/questing``, which is ``origin``'s copy of + the ``ubuntu/questing`` branch. +* We can also see a tag, ``ubuntu/2.2.0-0ubuntu1``. This means that someone + decided that commit ``f90b89b`` should have this "name" associated with it. + In Lubuntu's packaging repos, we use tags to keep track of which commits + correspond to which package version numbers. Whoever packaged ``lxqt-about`` + version ``2.2.0-0ubuntu1``, made this tag to say "if you check out + ``f90b89b``, you will see the packaging that I uploaded to Ubuntu when I + created version 2.2.0-0ubuntu1 of this package." +* Finally, what's this ``HEAD`` thing? In Git, the "head" is a pointer that + tracks whatever commit you are currently "on". In this instance, HEAD is on + commit ``fe38a66``. The ``->`` in ``HEAD -> ubuntu/resolute`` means that + HEAD is currently *attached* to the branch ``ubuntu/resolute``. That means + that if you make a new commit, not only will ``HEAD`` move to the new + commit, the commit will also be added to the ``ubuntu/resolute`` branch, + thus changing the tip of the branch. + +Now that we see what's going on with the log, let's take a look at the +repository's branches. We saw earlier that ``origin`` has an +``ubuntu/questing`` branch, so let's list off all the branches in the +repository and see what all we have:: + + git branch + +This will output:: + + * ubuntu/resolute + +Well that can't be right! There's an ``ubuntu/questing`` branch, so where is +it? To answer that question, we need to think a bit more about how Git works. + +* Every developer has their own copy of the repo, and the remote has a copy + too. +* Every developer can modify their repo as they see fit. They have to manually + upload and download changes that have been made to the repo on the remote. +* Developers can create and upload new branches. +* Therefore, **the remote repo might have branches your repo doesn't have.** + +Every time you download changes from the remote using ``git pull``, Git finds +out what branches the remote has available, downloads any commits associated +with them, and keeps track of those for you. However, it doesn't change the +branches in your repo. This makes it so that if you and someone else both make +a branch with the same name, you don't end up with serious problems the next +time both of you pull changes from the remote. Instead, Git keeps track of +your repo's branches and the remote repo's branches *separately*. + +When you see ``origin/ubuntu/resolute``, that's because there's actually a +branch named ``origin/ubuntu/resolute``. You also have a branch named +``ubuntu/resolute``, and that branch happens to be *tracking* +``origin/ubuntu/resolute``. That means that when changes to +``origin/ubuntu/resolute`` are downloaded, ``ubuntu/resolute`` gets updated +with those changes. It also means if you change ``ubuntu/resolute``, and +upload your changes, those changes will end up in ``origin/ubuntu/resolute``. +It's a two-way sync. + +With this in mind, we can better understand why ``ubuntu/questing`` is +missing; we don't have a branch named ``ubuntu/questing`` yet. We do know that +the ``origin`` remote has such a branch though. So let's see what branches +``origin`` has:: + + git branch -r + +This will output quite a bit more information:: + + origin/HEAD -> origin/ubuntu/resolute + origin/backports/focal + origin/backports/jammy + origin/backports/mantic + origin/ci/stable + origin/ci/unstable + origin/ubuntu/cosmic + origin/ubuntu/disco + origin/ubuntu/eoan + origin/ubuntu/focal + origin/ubuntu/groovy + origin/ubuntu/hirsute + origin/ubuntu/impish + origin/ubuntu/kinetic + origin/ubuntu/lunar + origin/ubuntu/mantic + origin/ubuntu/noble + origin/ubuntu/oracular + origin/ubuntu/plucky + origin/ubuntu/questing + origin/ubuntu/resolute + +We see that ``origin`` has a whole lot of branches, including the elusive +``origin/ubuntu/questing`` one we're looking for. There's also +``origin/HEAD``, which can be safely ignored. + +.. NOTE:: + If you're wondering what ``origin/HEAD`` is, see + ``__. + +Now, the question is, how do we add one of these remote branches to our repo? +To do this, we'll simply *check out* the branch:: + + git checkout ubuntu/questing + +This will output the following:: + + branch 'ubuntu/questing' set up to track 'origin/ubuntu/questing'. + Switched to a new branch 'ubuntu/questing' + +And now if we run ``git branch`` again, we'll see:: + + * ubuntu/questing + ubuntu/resolute + +Perfect! Now we have ``ubuntu/questing`` in our repo. We've also switched to +the ``ubuntu/questing`` branch, meaning our repo's working tree matches the +commit at the tip of ``ubuntu/questing``. To illustrate, let's look at a file +in the repository that will be different in ``ubuntu/questing`` and +``ubuntu/resolute``:: + + cat debian/watch + +This will output the following:: + + version=4 + opts="searchmode=plain, \ + pgpsigurlmangle=s/$/.asc/, \ + uversionmangle=s/(\d+\.\d+\.\d+).*/$1/" \ + https://api.github.com/repos/lxqt/@PACKAGE@/releases https:\/\/github.com\/lxqt\/@PACKAGE@\/releases\/download\/@ANY_VERSION@\/@PACKAGE@-@ANY_VERSION@.tar.xz + +We'll cover what all of this means once we get into Debian packaging itself. +For the time being though, let's switch back to ``ubuntu/resolute`` and see +what happens:: + + git checkout ubuntu/resolute + +This will output the following:: + + Switched to branch 'ubuntu/resolute' + Your branch is up to date with 'origin/ubuntu/resolute'. + +Now let's look at the ``debian/watch`` file again:: + + cat debian/watch + +This will output the following:: + + Version: 5 + Template: GitHub + Owner: lxqt + Project: @PACKAGE@ + Download-Url-Mangle: s%https://api.github.com/repos/([^/]+)/@PACKAGE@/git/refs/tags/@ANY_VERSION@%https://github.com/$1/@PACKAGE@/releases/download/$2/@PACKAGE@-$2.tar.xz%g + Pgp-Mode: auto + +That looks entirely different than what we saw a bit ago! What happened there? +We can find out what happened, by taking a look at the Git history. First, +let's find out who made the changes to this file:: + + git blame debian/watch + +This will show us a line-by-line breakdown of the file, indicating what +commits changed each line in the file, who made them, and when they made +them:: + + fe38a662 (Aaron Rainbolt 2025-11-18 13:16:56 -0600 1) Version: 5 + fe38a662 (Aaron Rainbolt 2025-11-18 13:16:56 -0600 2) Template: GitHub + fe38a662 (Aaron Rainbolt 2025-11-18 13:16:56 -0600 3) Owner: lxqt + fe38a662 (Aaron Rainbolt 2025-11-18 13:16:56 -0600 4) Project: @PACKAGE@ + fe38a662 (Aaron Rainbolt 2025-11-18 13:16:56 -0600 5) Download-Url-Mangle: s%https://api.github.com/repos/([^/]+)/@PACKAGE@/git/refs/tags/@ANY_VERSION@%https://github.com/$1/@PACKAGE@/releases/download/$2/@PACKAGE@-$2.tar.xz%g + fe38a662 (Aaron Rainbolt 2025-11-18 13:16:56 -0600 6) Pgp-Mode: auto + +See the ``fe38a662``? That's a partial commit ID! If we look back at the +output of ``git log`` from earlier, we can see the full commit ID, and +corresponding commit message:: + + commit fe38a662e5c7e0c87d31a25097e9dc29dfe23554 (HEAD -> ubuntu/resolute, origin/ubuntu/resolute) + Author: Aaron Rainbolt + Date: Tue Nov 18 13:16:56 2025 -0600 + + Use debian/watch version 5 (taken from Debian's packages) + +Now that we know who changed the file and when, let's see what their changes +looked like. Each Git commit corresponds to a *state* the repo was in when the +commit was made, so we can't simply say "show me commit XYZ". What we *can* +say is "show me everything that changed between commit ABC and commit XYZ". +Furthermore, Git allows us to refer to the commit *before* a particular commit +by adding a caret (``^``) after a commit ID. That is, ``fe38a662^`` refers to +the commit immediately *before* ``fe38a662``. Now that we have two commits to +compare to each other, we can tell Git to show us what changed between those +commits:: + + git diff fe38a662^ fe38a662 + +This will output the following:: + + diff --git a/debian/copyright b/debian/copyright + index 7b74a60..6ee271d 100644 + --- a/debian/copyright + +++ b/debian/copyright + @@ -11,10 +11,10 @@ License: LGPL-2.1+ + + Files: debian/* + Copyright: 2021-2025 Lubuntu Developers + - 2022 ChangZhuo Chen (陳昌倬) + 2014-2019 Alf Gaida + 2015-2021 Andrew Lee (李健秋) + 2025 Aaron Rainbolt + + 2022-2025 ChangZhuo Chen (陳昌倬) + License: LGPL-2.1+ + + License: LGPL-2.1+ + diff --git a/debian/watch b/debian/watch + index a544020..4070de7 100644 + --- a/debian/watch + +++ b/debian/watch + @@ -1,5 +1,6 @@ + -version=4 + -opts="searchmode=plain, \ + -pgpsigurlmangle=s/$/.asc/, \ + -uversionmangle=s/(\d+\.\d+\.\d+).*/$1/" \ + - https://api.github.com/repos/lxqt/@PACKAGE@/releases https:\/\/github.com\/lxqt\/@PACKAGE@\/releases\/download\/@ANY_VERSION@\/@PACKAGE@-@ANY_VERSION@.tar.xz + +Version: 5 + +Template: GitHub + +Owner: lxqt + +Project: @PACKAGE@ + +Download-Url-Mangle: s%https://api.github.com/repos/([^/]+)/@PACKAGE@/git/refs/tags/@ANY_VERSION@%https://github.com/$1/@PACKAGE@/releases/download/$2/@PACKAGE@-$2.tar.xz%g + +Pgp-Mode: auto + +We won't go into all the intricacies of the diff format used here. Suffice to +say, lines prefixed with a ``-`` are lines that were *removed*, and lines +prefixed with a ``+`` are lines that were *added*. In this case we can see +that ``debian/watch`` got completely rewritten. We also see that in +``debian/copyright``, ChangZhuo Chen was moved to the bottom of the +``Files: debian/*`` section, and the ``2022`` copyright reference was changed +to ``2022-2025``. Why exactly this was done, only the committer knows, but +that's what was done. The commit log conveniently lists Aaron's email, so now +you can now email Aaron Rainbolt and ask him why he did this. + +.. NOTE:: + The reason Aaron (me) made this change was because the ``debian/watch`` + file in this package was previously broken, and the Debian LXQt Team's + ``debian/watch`` file was working. I copied their file, and plugged it into + Lubuntu's packaging to get things to work right. The Git repo for Debian's + ``lxqt-about`` package listed ChangZhuo Chen as the author of the new watch + file contents, so I moved their copyright info to the bottom and updated + their copyright date accordingly. + +.. WARNING:: + If you're security-conscious, you might be asking yourself, "what's to stop + me from pretending to be Aaron or anyone else when I make a change to the + repo?" The answer is, **nothing prevents this by default.** Impersonating + others in Git logs is very easy. This is part of why projects don't hand + out Git commit access to just anyone; it would be too easy to do something + malicious and make someone else look like they were at fault. Even this + isn't bullet-proof though, a contributor might become malicious, or someone + might hack the server that hosts the remote Git repo. Thankfully, there is + a good way to make it impossible for others to impersonate you, which is to + PGP-sign your Git commits. More on that later. + +This is all well and good if we just want to analyze the differences between +individual commits. But what if we want to diff something else? Perhaps +someone recently pushed five commits to the ``ubuntu/resolute`` branch, and we +want to see what the repo looked like before and after their changes. There +are a few ways we can do this: + +* The most obvious way is to use ``git log`` to identify the commits we want + to compare, and then compare them with ``git diff``. For instance, if the + commit HEAD is on is ``12345678``, and the commit immediately before the + batch of changes is ``abcdefab``, we can compare them with + ``git diff abcdefab 12345678``. +* As a shortcut, if we want to refer to the HEAD commit, we don't have to type + out the full commit ID HEAD is on. We can just use ``HEAD``, like so: + ``git diff abcdefab HEAD``. +* We can also combine ``HEAD`` with ``^``. If we want to compare the commit + HEAD is on with the commit before it, we can use ``git diff HEAD^ HEAD``. + This only goes back one commit though, and we want to go back five, so... +* You can actually use ``^`` multiple times to go back more than one commit! + To compare the commit that is five commits back from HEAD, with the commit + HEAD is on, we can use ``git diff HEAD^^^^^ HEAD``. +* Typing ``^`` over and over can become cumbersome. As a shortcut, you can use + ``~N`` after a commit ID, where ``N`` is the number of commits to go + backward. This works with ``HEAD`` too, so we can use + ``git diff HEAD~5 HEAD``. This will give the exact same results as + ``git diff HEAD^^^^^ HEAD``. + +It's possible to diff "things" other than commits also, so long as those +"things" point at commits. Branches and tags can be compared to other +branches, other tags, commits IDs, or HEAD. Git calls all of these things +"refs". Diffing two refs will diff the commits the refs point to. + +Modifying a Git repo +^^^^^^^^^^^^^^^^^^^^ + +Most of the above covers how to look at existing commits and refs. What if we +want to make some changes to the repo and commit them? Let's try doing that +now, by changing the ``debian/control`` file. Open the file in a text editor +such as Featherpad, by running ``featherpad debian/control`` Scroll to the +bottom of the file, then change the description for ``lxqt-about-l10n`` to +say:: + + The about screen for LXQt + . + This package contains the l10n files needed by the lxqt-about. + It also contains an extra line in the description for qwerty. + +(You will be adding the last line to the description here. Mind the extra +space at the start of each line, those are intentional. We'll cover those +further when we get into packaging itself.) + +.. NOTE:: + You can use whatever text editor you want, but once you start doing a lot + of packaging, you'll probably want to learn how to use a text editor that + runs directly in the terminal. This is a lot more efficient than having to + switch between a terminal window and a GUI text editor. The editor + generally recommended for Lubuntu development is Vim, which comes + preinstalled on Lubuntu. You can get started with learning to use Vim by + running ``vimtutor`` in a terminal window. + +Save and close the file. Now let's see what Git thinks about what we've just +done, by running ``git status``. It will output the following:: + + On branch ubuntu/resolute + Your branch is up to date with 'origin/ubuntu/resolute'. + + Changes not staged for commit: + (use "git add ..." to update what will be committed) + (use "git restore ..." to discard changes in working directory) + modified: debian/control + + no changes added to commit (use "git add" and/or "git commit -a") + +Git sees that we modified the file, but claims that the changes we've made +aren't "staged for commit". In order to allow users to break up large changes +into multiple commits, Git uses the concept of a staging area (called the +"index"). Changes made to a repo have to be "staged" before they can be +committed, and when a new commit is made, only staged changes go into the +commit. This means if you make two unrelated changes to different parts of the +repo, you can stage one of those changes and commit it, while leaving the other +change unstaged. Once the first commit is made, you can stage and commit the +other change. + +Changes can be staged by using ``git add``. You'll generally want to use +``git add`` in one of three ways: + +* To stage an individual file, use ``git add path/to/file``. For instance, to + stage the changes we just made to ``debian/control``, you can use + ``git add debian/control``. +* To stage all of the files that changed within a directory, use + ``git add path/to/dir/*``. You can stage the changes made to + ``debian/control`` using ``git add debian/*``. Note that this recurses into + subdirectories; if ``debian/control`` and ``debian/upstream/metadata`` were + both changed, ``git add debian/*`` would stage both those files. +* Finally, to stage *all* changes made within the repo, use ``git add -A``. + This is probably the way you'll use ``git add`` the most. + +Now that we've staged the change, let's see what ``git status`` reports now:: + + On branch ubuntu/resolute + Your branch is up to date with 'origin/ubuntu/resolute'. + + Changes to be committed: + (use "git restore --staged ..." to unstage) + modified: debian/control + +We're now ready to commit a change finally! As one might expect, changes are +committed by using ``git commit``. + +* The easiest way to use this command is to just run ``git commit``; a text + editor will be opened, allowing you to write a description of the changes + you've made. This is good for if you need to write a long, detailed + explanation of what you've changed, but most of the time you won't need to + do that. +* If you only want a short description for the changes, you can give Git the + description by using ``git commit -m 'description here'``. + +Let's commit the change we just made, by running +``git commit -m 'update lxqt-about-l10n description'``. You should now see +something similar to the following:: + + [ubuntu/resolute 4686cc1] update lxqt-about-l10n description + 1 file changed, 1 insertion(+) + +Congratulations, you have now committed your first change! If we look at +``git status`` now, we'll see:: + + On branch ubuntu/resolute + Your branch is ahead of 'origin/ubuntu/resolute' by 1 commit. + (use "git push" to publish your local commits) + + nothing to commit, working tree clean + +This should all look familiar by now, except for the line +``Your branch is ahead of 'origin/ubuntu/resolute' by 1 commit.`` This means +that our copy of ``ubuntu/resolute`` has one commit in it that ``origin``'s +copy of ``ubuntu/resolute`` doesn't have yet. + +Let's see what happens if we create a new file in the repo. Make sure you're +in the ``lxqt-about-packaging`` directory, and then create a file named +``test.md`` with the following contents:: + + # Test file + + This is a test file. + +Save and close the file, then run ``git status`` again. You should see:: + + On branch ubuntu/resolute + Your branch is ahead of 'origin/ubuntu/resolute' by 1 commit. + (use "git push" to publish your local commits) + + Untracked files: + (use "git add ..." to include in what will be committed) + test.md + + nothing added to commit but untracked files present (use "git add" to track) + +This is a bit different than what we saw earlier. That's because previously, +we modified a file Git already knew about in this repo, namely +``debian/control``. Git does not yet know that ``test.md`` is actually part of +the repo, so it's considered *untracked*. Git treats untracked files and +unstaged changes to tracked files in subtly different ways we'll investigate +later, but for now, you can mostly treat untracked files the same way you'd +treat any other modified file. We don't actually need to commit this file to +continue learning how Git works, so go ahead and delete the file with +``rm test.md``. + +Inevitably, as you work on packaging, you are going to make mistakes, and +inevitably, you're going to end up committing some of those mistakes. Let's +look at the line we added to ``debian/control`` a bit ago:: + + It also contains an extra line in the description for qwerty. + +This probably looked a bit strange to you. "qwerty" really doesn't describe +what we're doing accurately. "testing" would be a better word to use. Go ahead +and change ``qwerty`` to ``testing`` now, using Featherpad or your preferred +text editor. Stage the change with ``git add -A`` when you're done. + +Now, we *could* just add the fix as a new commit. However, in this instance, +that's probably not the best thing to do since the commit exists only on your +computer. No one else knows that we wrote "qwerty" when we meant "testing", so +we can simply recreate the commit. To do that, use +``git commit --amend --no-edit``. This will discard the previous commit, and +create a new one that contains all of the changes from the previous commit +plus all of the changes in the index. It will also preserve our commit message +unchanged (that's what ``--no-edit`` does). If you want to change the commit +message too (or if the only thing you want to change is the commit message), +you can leave off ``--no-edit`` to get a commit message editor, or you can use +``-m`` to pass a one-line commit message, just like you would when using ``git +commit`` normally. + +.. NOTE:: + I intentionally avoided saying that we're *changing* the commit here. + Commits are immutable, and each commit links to the commit immediately + behind it. This allows Git to provides a number of helpful features, but it + also means that one shouldn't think in terms of "changing" a commit. + +.. NOTE:: + ``git commit --amend`` is useful for recreating the last commit. But what + if we need to fix something from several commits ago? Because commits are + linked together and immutable, we have to recreate the bad commit and every + commit ahead of it. Git provides a utility for this, ``git rebase``, but + you'll rarely need it and it is tricky to use, so we won't cover this yet. + +Amending a commit is useful for fixing something wrong with a commit. But what +if the commit is so far gone you'd rather just get rid of it and start over? +This extra line in the description of ``lxqt-about-l10n`` isn't really useful, +and it's probably not something that can be made useful in the future. This is +where ``git reset`` comes in handy. Whereas ``git commit`` creates commits, +``git reset`` destroys them. You can use ``git reset`` in a few different +ways, the two most useful are: + +* Mixed reset, which gets rid of commits but leaves the actual changes made in + those commits in your working tree. Mixed reset is the default mode. +* Hard reset, which gets rid of commits *and* resets all tracked files to + a previous state. Note well that this resets *tracked* files. Files that are + untracked will be left alone even by a hard reset. This is one of the ways + Git treats tracked and untracked files differently. + +To do a mixed reset to the previous commit, run:: + + git reset HEAD^ + +This will leave our changes intact, but the commit itself will be undone and +the changes will *not* be staged for commit. You can now make more changes and +then make a new commit, or you can completely undo all of our changes with:: + + git add -A + git reset --hard HEAD + +This will first stage all modifications, so that any untracked files are +caught. Then it will hard-reset the repo back to the commit HEAD is pointing +to, discarding our changes. + +When you're doing work in a Git repo, it's generally a good idea to work on a +branch other than the "primary" branch (``ubuntu/resolute`` in this instance). +This makes it easier to avoid running into conflicts with other developers. +It is possible to use ``git branch`` to create branches, but most of the time +when you're making a branch, you also want to switch to the branch. For this, +you can use the ``-b`` option of ``git checkout``, like so:: + + git checkout -b my-new-branch + +Note that ``git checkout -b`` will fail if the branch already exists. + +This is a good enough start for now. There are a few more things you'll want +to know about Git in your work as a packager (namely rebasing, tagging, +forking, and pushing), but it will be easiest to cover those as we need them. + +Miscellaneous useful things to know about Git +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* You can diff the contents of your working tree with the HEAD commit by using + ``git diff HEAD``. This is useful to see what all you've changed before + committing it. Note that untracked files will be ignored when you do this. + If you want to include those in the diff too, ``git add`` them first. +* Raw diffs are oftentimes hard to read. If you want a more comfortable + viewing experience, install Meld (``sudo apt install meld``), then use + ``git difftool --tool=meld --dir-diff`` instead of ``git diff``. This will + let you visualize the changes between two commits in much more detail than a + raw diff allows. You can even make (limited) changes to your working tree + here by modifying files on the right side of the Meld window. (Be warned + that changes made to the left side of the window will be silently discarded, + and that copying files from the left side of the window to the right side + will not work; those files will be silently discarded as well.) +* You can checkout more than just branches. ``git checkout`` can be used on + any ref. If you check out something other than a branch however, you will + enter what Git calls "detached HEAD state". This means that any commits you + make will not be integrated into any existing branch, and if you check + something else out later, you'll probably have a hard time finding those + commits later. If you decide you want to keep the changes you've made, use + ``git switch -c new-branch-name`` to make a new branch with any changes + you've made so far. +* As a general rule, do not use force-push unless you're fixing commits you + just pushed. Other users may have pulled those commits, and will have to fix + their repos if you do this. There are exception to this rule, you'll be able + to spot them as you gain experience. + +Foundations of Debian packaging +------------------------------- + +TODO