History

feat: Add `Repository::commit_as(committer, author, …)` convenience method.

That way it's, very much beyond convenience, possible to set the time of a commit. Many thanks to @epage for the suggestion.

Sebastian Thiel committed 3 years ago

Commit 8482f901 parent fd287533 files · +73 −18Browse files

Unified · Split

3 files
  1. etc/check-package-size.sh +1 −1
  2. git-repository/src/repository/object.rs +36 −17
  3. git-repository/tests/repository/object.rs +36 −0
etc/check-package-size.sh+1−1
@@ -53,7 +53,7 @@ echo "in root: gitoxide CLI" · 52 unmodified lines
5353 (enter git-prompt && indent cargo diet -n --package-size-limit 15KB)
5454 (enter git-object && indent cargo diet -n --package-size-limit 25KB)
5555 (enter git-commitgraph && indent cargo diet -n --package-size-limit 30KB)
56−(enter git-pack && indent cargo diet -n --package-size-limit 120KB)
56+(enter git-pack && indent cargo diet -n --package-size-limit 125KB)
5757 (enter git-odb && indent cargo diet -n --package-size-limit 130KB)
5858 (enter git-protocol && indent cargo diet -n --package-size-limit 60KB)
5959 (enter git-packetline && indent cargo diet -n --package-size-limit 35KB)
git-repository/src/repository/object.rs+36−17
@@ -124,21 +124,13 @@ impl crate::Repository { · 123 unmodified lines
124124 self.tag_reference(name, tag_id, constraint).map_err(Into::into)
125125 }
126126
127− /// Create a new commit object with `message` referring to `tree` with `parents`, and point `reference`
128− /// to it. The commit is written without message encoding field, which can be assumed to be UTF-8.
129− /// `author` and `committer` fields are pre-set from the configuration, which can be altered
130− /// [temporarily][crate::Repository::config_snapshot_mut()] before the call if required.
131− ///
132− /// `reference` will be created if it doesn't exist, and can be `"HEAD"` to automatically write-through to the symbolic reference
133− /// that `HEAD` points to if it is not detached. For this reason, detached head states cannot be created unless the `HEAD` is detached
134− /// already. The reflog will be written as canonical git would do, like `<operation> (<detail>): <summary>`.
135− ///
136− /// The first parent id in `parents` is expected to be the current target of `reference` and the operation will fail if it is not.
137− /// If there is no parent, the `reference` is expected to not exist yet.
127+ /// Similar to [`commit(…)`][crate::Repository::commit()], but allows to create the commit with `committer` and `author` specified.
138128 ///
139− /// The method fails immediately if a `reference` lock can't be acquired.
140− pub fn commit<Name, E>(
129+ /// This forces setting the commit time and author time by hand. Note that typically, committer and author are the same.
130+ pub fn commit_as<'a, 'c, Name, E>(
141131 &self,
132+ committer: impl Into<git_actor::SignatureRef<'c>>,
133+ author: impl Into<git_actor::SignatureRef<'a>>,
142134 reference: Name,
143135 message: impl AsRef<str>,
144136 tree: impl Into<ObjectId>,
@@ -156,13 +148,11 @@ impl crate::Repository { · 11 unmodified lines
156148 // TODO: possibly use CommitRef to save a few allocations (but will have to allocate for object ids anyway.
157149 // This can be made vastly more efficient though if we wanted to, so we lie in the API
158150 let reference = reference.try_into()?;
159− let author = self.author_or_default();
160− let committer = self.committer_or_default();
161151 let commit = git_object::Commit {
162152 message: message.as_ref().into(),
163153 tree: tree.into(),
164− author: author.to_owned(),
165− committer: committer.to_owned(),
154+ author: author.into().to_owned(),
155+ committer: committer.into().to_owned(),
166156 encoding: None,
167157 parents: parents.into_iter().map(|id| id.into()).collect(),
168158 extra_headers: Default::default(),
@@ -194,6 +184,35 @@ impl crate::Repository { · 25 unmodified lines
194184 Ok(commit_id)
195185 }
196186
187+ /// Create a new commit object with `message` referring to `tree` with `parents`, and point `reference`
188+ /// to it. The commit is written without message encoding field, which can be assumed to be UTF-8.
189+ /// `author` and `committer` fields are pre-set from the configuration, which can be altered
190+ /// [temporarily][crate::Repository::config_snapshot_mut()] before the call if required.
191+ ///
192+ /// `reference` will be created if it doesn't exist, and can be `"HEAD"` to automatically write-through to the symbolic reference
193+ /// that `HEAD` points to if it is not detached. For this reason, detached head states cannot be created unless the `HEAD` is detached
194+ /// already. The reflog will be written as canonical git would do, like `<operation> (<detail>): <summary>`.
195+ ///
196+ /// The first parent id in `parents` is expected to be the current target of `reference` and the operation will fail if it is not.
197+ /// If there is no parent, the `reference` is expected to not exist yet.
198+ ///
199+ /// The method fails immediately if a `reference` lock can't be acquired.
200+ pub fn commit<Name, E>(
201+ &self,
202+ reference: Name,
203+ message: impl AsRef<str>,
204+ tree: impl Into<ObjectId>,
205+ parents: impl IntoIterator<Item = impl Into<ObjectId>>,
206+ ) -> Result<Id<'_>, commit::Error>
207+ where
208+ Name: TryInto<FullName, Error = E>,
209+ commit::Error: From<E>,
210+ {
211+ let author = self.author_or_default();
212+ let committer = self.committer_or_default();
213+ self.commit_as(committer, author, reference, message, tree, parents)
214+ }
215+
197216 /// Return an empty tree object, suitable for [getting changes](crate::Tree::changes()).
198217 ///
199218 /// Note that it is special and doesn't physically exist in the object database even though it can be returned.
git-repository/tests/repository/object.rs+36−0
@@ -147,6 +147,42 @@ mod tag { · 146 unmodified lines
147147 }
148148 }
149149
150+mod commit_as {
151+ use git_repository as git;
152+ use git_testtools::tempfile;
153+
154+ #[test]
155+ fn specify_committer_and_author() -> crate::Result {
156+ let tmp = tempfile::tempdir().unwrap();
157+ let repo = git::init(&tmp).unwrap();
158+ let empty_tree = repo.empty_tree();
159+ let committer = git::actor::Signature {
160+ name: "c".into(),
161+ email: "c@example.com".into(),
162+ time: git::actor::Time::new(1, 1800),
163+ };
164+ let author = git::actor::Signature {
165+ name: "a".into(),
166+ email: "a@example.com".into(),
167+ time: git::actor::Time::new(3, 3600),
168+ };
169+
170+ let commit_id = repo.commit_as(
171+ &committer,
172+ &author,
173+ "HEAD",
174+ "initial",
175+ empty_tree.id,
176+ git::commit::NO_PARENT_IDS,
177+ )?;
178+ let commit = commit_id.object()?.into_commit();
179+
180+ assert_eq!(commit.committer()?, committer.to_ref());
181+ assert_eq!(commit.author()?, author.to_ref());
182+ Ok(())
183+ }
184+}
185+
150186 mod commit {
151187 use git_repository as git;
152188 use git_testtools::{hex_to_id, tempfile};